book

02. 객체지향 프로그래밍

2025-11-20
3분 분량
오브젝트

1. 영화 예매 시스템 요구사항

먼저 영화 예매 시스템이 가져야 할 기능과 규칙을 정리한다.

  • 고객은 영화를 예매할 수 있다
  • 영화는 제목, 상영 시간, 가격 등의 정보를 가진다
  • 할인 조건(요일, 시간, 순번 등)에 따라 요금이 달라질 수 있다
  • 할인 정책은 금액 할인과 비율 할인 등이 존재한다

요구사항을 분석한 후, 이를 객체로 모델링하기 위한 협력 구조를 설정한다


2. 객체지향 프로그래밍을 향해

책에서는 협력 → 객체 → 클래스 순으로 객체지향 설계가 전개된다고 설명한다.

1) 협력, 객체, 클래스

객체지향의 출발점은 협력이다.

협력을 위해 객체가 필요하고, 객체를 만들기 위해 클래스가 존재한다.

즉, 클래스는 협력을 지원하기 위한 구현 도구이다.


3. 도메인의 구조를 따르는 프로그램 구조

도메인에서 식별되는 주요 개념을 그대로 코드의 클래스로 만든다.

2장에서는 다음의 핵심 클래스들이 등장한다.

  • Movie
  • Screening
  • Reservation
  • Customer
  • DiscountPolicy (추상)
  • DiscountCondition (인터페이스)

이 구조는 도메인 개념과 일치하기 때문에 코드의 가독성과 변경 용이성이 높아진다


4. 클래스 구현하기

Movie

할인 정책(DiscountPolicy)을 이용해 가격을 계산한다.

java
public class Movie {
    private String title;
    private Duration runningTime;
    private Money fee;
    private DiscountPolicy discountPolicy;

    public Money calculateMovieFee(Screening screening) {
        return fee.minus(discountPolicy.calculateDiscountAmount(screening));
    }
}

Movie는 DiscountPolicy에 메시지를 보낼 뿐 정책의 종류나 방식은 알지 못한다.


Screening

상영 정보는 Movie에게 가격 계산을 위임한다.

java
public class Screening {
    private Movie movie;
    private int sequence;
    private LocalDateTime whenScreened;

    public Reservation reserve(Customer customer, int audienceCount) {
        Money fee = movie.calculateMovieFee(this).times(audienceCount);
        return new Reservation(customer, this, fee, audienceCount);
    }
}

Screening에서 Movie를 호출하는 구조가 협력의 대표적인 예이다.


5. 협력하는 객체들의 공동체

객체는 서로 메시지를 주고받으며 협력을 수행한다.

예매 과정에서는 다음 메시지 흐름이 발생한다.

  1. Customer가 Screening에 reserve 요청
  2. Screening이 Movie에 calculateMovieFee 요청
  3. Movie가 DiscountPolicy에 calculateDiscountAmount 요청
  4. DiscountPolicy가 DiscountCondition을 통해 할인 여부 판단

이처럼 협력 관계 안에서 책임이 분산된다


6. 할인 요금 구하기

할인 정책(DiscountPolicy)은 템플릿 메서드 패턴과 유사한 구조로 구성된다.

DiscountPolicy (추상 클래스)

java
public abstract class DiscountPolicy {
    private List<DiscountCondition> conditions;

    public Money calculateDiscountAmount(Screening screening) {
        for (DiscountCondition condition : conditions) {
            if (condition.isSatisfiedBy(screening)) {
                return getDiscountAmount(screening);
            }
        }
        return Money.ZERO;
    }

    protected abstract Money getDiscountAmount(Screening screening);
}

조건이 만족되면 구체 정책이 금액을 계산한다.


금액 할인 정책

java
public class AmountDiscountPolicy extends DiscountPolicy {
    private Money discountAmount;

    @Override
    protected Money getDiscountAmount(Screening screening) {
        return discountAmount;
    }
}

비율 할인 정책

java
public class PercentDiscountPolicy extends DiscountPolicy {
    private double percent;

    @Override
    protected Money getDiscountAmount(Screening screening) {
        return screening.getMovieFee().times(percent);
    }
}


7. 할인 조건

DiscountCondition은 특정 요건이 충족되었는지를 판단한다.

SequenceCondition

java
public class SequenceCondition implements DiscountCondition {
    private int sequence;

    public boolean isSatisfiedBy(Screening screening) {
        return screening.getSequence() == sequence;
    }
}

PeriodCondition

java
public class PeriodCondition implements DiscountCondition {
    private DayOfWeek dayOfWeek;
    private LocalTime start;
    private LocalTime end;

    public boolean isSatisfiedBy(Screening screening) {
        LocalDateTime when = screening.getWhenScreened();
        return when.getDayOfWeek().equals(dayOfWeek) &&
               !when.toLocalTime().isBefore(start) &&
               !when.toLocalTime().isAfter(end);
    }
}

이 구조는 정책과 조건을 조합하여 다양한 할인 규칙을 만들 수 있는 유연성을 가진다


8. 상속과 다형성

할인 정책은 DiscountPolicy를 상속하여 서로 다른 할인 전략을 구현한다.

calculateDiscountAmount는 공통 흐름을 제공하고, 구체 할인 방식은 getDiscountAmount에서 정의된다.

이는 차이에 의한 프로그래밍 및 다형성 기반 확장의 예이다


결론

2장은 다음 사실을 정리해 준다.

  1. 객체지향 프로그래밍은 협력으로부터 객체를 도출하고, 객체로부터 클래스를 만든다
  2. 도메인의 구조를 코드에 그대로 반영해야 변경에 강해진다
  3. 메시지를 기반으로 객체는 서로 협력한다
  4. 상속과 다형성을 이용하면 다양한 정책과 조건을 확장할 수 있다

함께 읽으면 좋은 글