Skip to main content

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. 🤔 이해가 어려운 부분

🔍 질문하기

  1. 디미터 법칙을 위반하는 코드와 이를 개선한 코드의 차이는 무엇인가?
    • 위에서 설명한 getEngine().start(); 같은 직접적인 내부 구조 노출이 문제다.
    • 개선 코드에서는 car.startEngine();처럼 객체가 직접 행동을 수행하도록 변경했다.

3. 📚 참고 사항

📢 논의하기

객체 지향 vs 절차 지향

책에서는 객체 지향이 항상 최선이 아니며, 절차 지향이 적합한 경우도 있다고 말한다. 그렇다면 언제 절차 지향이 더 적합할까?

📌 절차 지향이 적합한 경우

  1. 단순한 데이터 처리 → 객체 간의 복잡한 상호작용이 필요하지 않은 경우.
  2. 일회성 스크립트 → 데이터를 가져와 가공하고 저장하는 간단한 작업.
  3. 성능이 중요한 경우 → 불필요한 객체 생성을 줄이고, 순차적인 처리가 유리한 경우.
  4. 기능 중심의 코드 → 데이터보다는 로직이 중심이 되는 경우.

📌 결론: 모든 상황에서 객체 지향이 정답은 아니다. 변화의 방향에 따라 절차 지향이 더 적합할 수도 있다!