6장 객체와 자료 구조
1. 📌 핵심 개념 정리
✅ 요약하기
1. 자료 추상화
추상 인터페이스를 제공해 사용자가 구현을 모른 채 자료의 핵심을 조작할 수 있어야 진정한 의미의 클래스다. 단순히 인터페이스나 조회/설정 함수만 제공한다고 해서 추상화가 이루어지는 것이 아니다.
- 변수 사이에 함수라는 계층을 넣는다고 구현이 감춰지지는 않는다. 구현을 감추려면 추상화가 필요하다.
- 단순한 조회 함수와 설정 함수로 변수를 다루는 것이 아니라, 추상적인 개념으로 표현하는 것이 좋다.
- 개발자는 객체가 포함하는 자료를 표현할 가장 좋은 방법을 고민해야 한다.
- 무작정 조회/설정 함수를 추가하는 것은 가장 나쁜 방법이다.
2. 자료/객체 비대칭
객체와 자료 구조는 본질적으로 서로 반대되는 개념이다.
-
객체(Object): 자료를 숨기고 동작(메서드)을 공개한다.
- 기존 객체에 새로운 타입을 추가하기 쉽다.
- 반면, 기존 객체에 새로운 동작을 추가하는 것은 어렵다.
-
자료 구조(Data Structure): 별다른 동작 없이 자료를 노출한다.
- 기존 자료 구조에 새로운 동작을 추가하기 쉽다.
- 반면, 기존 동작에 새로운 자료 구조를 추가하는 것은 어렵다.
📌 예제 코드
절차적 코드 (자료 구조 중심)
public class Square {
public Point topLeft;
public double side;
}
public class Geometry {
public final double PI = 3.141592653589793;
public double area(Object shape) throws NoSuchShapeException {
if (shape instanceof Square) {
Square s = (Square) shape;
return s.side * s.side;
}
throw new NoSuchShapeException();
}
}
- 새로운 도형을 추가하면
Geometry
클래스를 수정해야 한다. - 하지만
area()
같은 기존 함수에 새로운 동작을 추가하는 것은 쉽다.
객체 지향 코드 (객체 중심)
public class Square implements Shape {
private Point topLeft;
private double side;
public double area() {
return side * side;
}
}
- 새로운 도형을 추가해도 기존 클래스는 변경할 필요가 없다.
- 하지만
perimeter()
같은 새로운 동작을 추가하려면 모든 도형 클래스를 수정해야 한다.
정리
- 새로운 함수가 필요한 경우 → 절차 지향적인 코드(자료 구조 중심)
- 새로운 자료 타입이 필요한 경우 → 객체 지향적인 코드(객체 중심)
3. 디미터 법칙
객체는 자신이 조작하는 객체의 내부를 몰라야 한다.
📌 기차 충돌 (Train Wreck)
final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
- 여러 객체가 한 줄로 이어진 기차처럼 보이는 코드.
- 내부 자료 구조가 외부로 노출됨.
📌 디미터 법칙을 위반한 예제 코드
class Engine {
public void start() {
System.out.println("Engine started");
}
}
class Car {
private Engine engine = new Engine();
public Engine getEngine() { // 내부 객체를 직접 반환
return engine;
}
}
class Driver {
public void drive(Car car) {
car.getEngine().start(); // 🚨 디미터 법칙 위반
}
}
📌 디미터 법칙을 준수한 코드
class Car {
private Engine engine = new Engine();
public void startEngine() { // 중간 계층을 제공
engine.start();
}
}
class Driver {
public void drive(Car car) {
car.startEngine(); // ✅ 디미터 법칙 준수
}
}
- 내부 자료를 감추고 필요한 동작만 제공해야 한다.
4. 자료 전달 객체 (DTO, Data Transfer Object)
DTO는 데이터베이스에서 가져온 가공되지 않은 정보를 애플리케이션 코드에서 사용할 객체로 변환하는 데 사용된다.
📌 DTO 예제 코드
public class Address {
private String street;
private String city;
private String state;
private String zip;
public Address(String street, String city, String state, String zip) {
this.street = street;
this.city = city;
this.state = state;
this.zip = zip;
}
}
활성 레코드 (Active Record)
- DTO의 특수한 형태로 데이터 + 데이터베이스 관련 기능을 포함.
save()
,find()
같은 탐색 함수 제공.- 하지만 비즈니스 로직을 포함하면 안 된다 → 잡종 구조가 되기 때문.
🚨 해결책: 활성 레코드는 자료 구조로 취급하고, 비즈니스 규칙을 담을 별도의 객체를 만든다.
2. 🤔 이해가 어려운 부분
🔍 질문하기
- 디미터 법칙을 위반하는 코드와 이를 개선한 코드의 차이는 무엇인가?
- 위에서 설명한
getEngine().start();
같은 직접적인 내부 구조 노출이 문제다. - 개선 코드에서는
car.startEngine();
처럼 객체가 직접 행동을 수행하도록 변경했다.
- 위에서 설명한
3. 📚 참고 사항
📢 논의하기
객체 지향 vs 절차 지향
책에서는 객체 지향이 항상 최선이 아니며, 절차 지향이 적합한 경우도 있다고 말한다. 그렇다면 언제 절차 지향이 더 적합할까?
📌 절차 지향이 적합한 경우
- 단순한 데이터 처리 → 객체 간의 복잡한 상호작용이 필요하지 않은 경우.
- 일회성 스크립트 → 데이터를 가져와 가공하고 저장하는 간단한 작업.
- 성능이 중요한 경우 → 불필요한 객체 생성을 줄이고, 순차적인 처리가 유리한 경우.
- 기능 중심의 코드 → 데이터보다는 로직이 중심이 되는 경우.
📌 결론: 모든 상황에서 객체 지향이 정답은 아니다. 변화의 방향에 따라 절차 지향이 더 적합할 수도 있다!