book

01. 객체, 설계

2025-11-18
2분 분량
오브젝트

1. 티켓 판매 애플리케이션의 초기 구현

가장 처음 제시되는 버전의 코드는 Theater가 모든 작업을 도맡아 처리하는 형태이다.

Theater는 Audience, Bag, TicketSeller, TicketOffice의 세부 구현을 모두 알고 있다.

초기 코드 예시 (절차적 설계)

java
public class Theater {
    private TicketSeller ticketSeller;

    public Theater(TicketSeller ticketSeller) {
        this.ticketSeller = ticketSeller;
    }

    public void enter(Audience audience) {
        if (audience.getBag().hasInvitation()) {
            Ticket ticket = ticketSeller.getTicketOffice().getTicket();
            audience.getBag().setTicket(ticket);
        } else {
            Ticket ticket = ticketSeller.getTicketOffice().getTicket();
            audience.getBag().minusAmount(ticket.getFee());
            ticketSeller.getTicketOffice().plusAmount(ticket.getFee());
            audience.getBag().setTicket(ticket);
        }
    }
}

이 구조는 다음과 같은 특징을 가진다.

  • Theater는 Audience의 Bag 내부 구조를 알고 직접 조작한다
  • TicketSeller 내부에 TicketOffice가 존재한다는 사실을 알고 있다
  • TicketOffice의 금액 처리 방식까지 알고 있다

책에서는 이러한 상황을 예상에 어긋나는 코드, 변경에 취약한 코드라고 설명한다.


2. 무엇이 문제인가

1) 변화에 취약한 이유

Theater가 여러 객체의 내부 구현에 직접 접근하고 있기 때문에 내부 구조가 조금만 바뀌어도 Theater의 코드를 반드시 수정해야 한다.

이는 높은 결합도로 인해 변경을 고립시키기 어렵기 때문이다

2) 객체가 자율적이지 못함

Audience나 TicketSeller는 데이터 보관소처럼 존재할 뿐, 스스로 행위를 하지 않는다.

현실 모델과 괴리되어 읽기 어렵고, 객체의 책임이 명확하지 않다.


3. 설계 개선하기 — 책임의 이동

개선 과정의 핵심은 Theater에 집중된 책임을 적절한 객체에게 분산하는 것이다.

Audience가 스스로 티켓을 구매하고, TicketSeller가 스스로 판매를 수행하도록 책임을 옮긴다.


4. 개선된 객체지향 설계 코드

개선된 설계에서는 Theater는 단지 “판매를 요청하는 역할”만 수행한다.

Audience

java
public class Audience {
    private Bag bag;

    public Audience(Bag bag) {
        this.bag = bag;
    }

    public Long buy(Ticket ticket) {
        return bag.hold(ticket);
    }
}

Bag

java
public class Bag {
    private Long amount;
    private Invitation invitation;
    private Ticket ticket;

    public Long hold(Ticket ticket) {
        if (invitation != null) {
            this.ticket = ticket;
            return 0L;
        } else {
            this.ticket = ticket;
            this.amount -= ticket.getFee();
            return ticket.getFee();
        }
    }
}

TicketSeller

java
public class TicketSeller {
    private TicketOffice ticketOffice;

    public TicketSeller(TicketOffice ticketOffice) {
        this.ticketOffice = ticketOffice;
    }

    public void sellTo(Audience audience) {
        Ticket ticket = ticketOffice.getTicket();
        long fee = audience.buy(ticket);
        ticketOffice.plusAmount(fee);
    }
}

Theater

java
public class Theater {
    private TicketSeller ticketSeller;

    public Theater(TicketSeller ticketSeller) {
        this.ticketSeller = ticketSeller;
    }

    public void enter(Audience audience) {
        ticketSeller.sellTo(audience);
    }
}


5. 무엇이 개선되었는가

책에서는 개선된 구조의 변화점을 다음과 같이 설명한다.

1) 캡슐화

Audience 내부의 Bag 구조나 TicketSeller 내부의 TicketOffice 구조가 외부에 드러나지 않는다

(캡슐화 설명: 오브젝트_북마크).

2) 응집도 증가

  • Audience는 “티켓을 구매하는 행동”을
  • TicketSeller는 “티켓을 판매하는 행동”을
  • Bag은 “티켓 보관 및 금액 계산을 처리하는 행동”을

3) 결합도 감소

Theater는 오직 TicketSeller의 sellTo 메시지만 알면 된다.

Audience, Bag, TicketOffice의 내부 구현과 무관하게 변경이 가능해진다.

4) 책임 중심 설계

하나의 기능을 수행하기 위한 책임이 여러 객체에 자연스럽게 분산되고, 객체는 스스로 자신의 데이터를 책임진다


6. 설계에 대한 이해

1장의 마지막에서는 “설계란 무엇인가”에 대한 정의를 다음과 같이 제시한다.

설계란 코드를 배치하는 것이다

그리고 좋은 설계란 다음 두 가지 요구를 동시에 만족하는 설계라고 설명한다.

  • 오늘의 요구사항을 충족하는 코드
  • 내일의 변경을 수용할 수 있는 코드

결론

1장은 단순한 예제를 통해 다음을 자연스럽게 보여준다.

  • 절차적 설계는 변경에 취약하다
  • 객체지향 설계는 객체가 스스로 책임을 수행해야 한다
  • 캡슐화, 응집도, 결합도는 설계를 평가하는 핵심 기준이다
  • 객체지향 설계의 목표는 자율적인 객체들의 협력 구조를 만드는 것이다

함께 읽으면 좋은 글