Chapter 1

전략 패턴과 옵저버 패턴

  • 1.1 SimUduck 오리 시뮬레이션
  • 1.2 상속의 한계
  • 1.3 바뀌는 부분을 캡슐화하라
  • 1.4 전략 패턴
  • 1.5 기상 모니터링 애플리케이션
  • 1.6 옵저버 패턴 정의
  • 1.7 느슨한 결합의 위력
  • 1.8 풀 방식 vs 푸시 방식

객체지향 설계의 첫 번째 교훈은 "바뀌는 부분을 분리하라"이고, 두 번째는 "서로를 모르게 만들라"야.

SimUduck이라는 오리 시뮬레이션 게임을 생각해보자. Duck 슈퍼클래스에 quack(), swim(), display()가 있고, 서브클래스들이 display()만 오버라이드하면 잘 돌아가는 깔끔한 구조였거든. 그런데 "오리도 날게 해!"라는 요구가 들어오는 순간 모든 게 무너지지. 슈퍼클래스에 fly()를 추가하면 고무 오리도 날아다니는 사고가 터나고, 오버라이드로 막으면 나무 오리가 추가될 때 또 막아야 하고, 48종의 오리가 있으면 48번 점검해야 하는 유지보수 지옥이 펼쳐져. 그렇다고 Flyable, Quackable 같은 인터페이스를 만들면 코드 재사용이 안 되잖아. 상속은 부작용이 크고, 인터페이스는 중복이 심하고 — 둘 다 답이 아닌 거야.

이 딜레마를 푸는 열쇠가 "바뀌는 부분을 찾아내서 바뀌지 않는 부분과 분리하라"는 원칙이야. fly()quack()은 오리마다 다르니까, 이걸 Duck 클래스에서 빼내서 FlyBehavior, QuackBehavior라는 별도의 인터페이스와 구현 클래스들로 만드는 거지. Duck은 이 행동 객체를 인스턴스 변수로 들고 있고, performFly()를 호출하면 flyBehavior.fly()에 위임해. 오리가 직접 나는 게 아니라, 나는 행동 객체한테 시키는 구조야. 이게 **전략 패턴(Strategy Pattern)**이야. 핵심은 **"상속(is-a)보다 구성(has-a)을 활용하라"**는 거거든. Duck has-a FlyBehavior 관계가 되면, 런타임에도 setFlyBehavior(new FlyRocketPowered())로 행동을 갈아끼울 수 있어. 상속으로는 절대 불가능한 유연함이지.

전략 패턴이 "바뀌는 행동을 분리"하는 거였다면, 이번엔 상태 변화를 알리는 문제를 풀어보자. 기상 스테이션 WeatherData가 온도, 습도, 기압을 수집하고 여러 디스플레이에 보여줘야 하는 상황이야. 처음엔 measurementsChanged() 안에서 각 디스플레이의 update()를 직접 호출하겠지. 작동은 하지만, 디스플레이가 추가될 때마다 이 메소드를 수정해야 하고, 런타임에 디스플레이를 추가하거나 제거할 수도 없어. 구체적인 구현에 딱 달라붙은 코드야.

**옵저버 패턴(Observer Pattern)**은 이걸 신문 구독 모델로 풀어. 신문사(Subject)가 발행하면 구독자(Observer)들이 자동으로 받고, 구독 해지하면 안 받는 거지. Subject는 옵저버가 Observer 인터페이스를 구현한다는 것만 알면 돼 — 구체적으로 어떤 클래스인지, 뭘 하는지 전혀 모른다. WeatherDatanotifyObservers()만 호출하면 되고, 새 디스플레이가 추가돼도 코드를 수정할 필요가 없어져. 이게 바로 **느슨한 결합(Loose Coupling)**의 위력이야. Subject와 Observer는 서로의 내부를 모르니까, 독립적으로 변경하고 확장할 수 있거든. 여기에 한 가지 더 — 데이터를 Subject가 밀어주는 푸시 방식보다, 알림만 보내고 옵저버가 필요한 데이터를 직접 가져가는 풀 방식이 더 유연해. Subject에 새 데이터가 추가돼도 update() 시그니처를 바꿀 필요가 없으니까. 결국 핵심은 하나야 — 상호작용하는 객체 사이에는 가능하면 서로를 모르게 만들어라.


정리

1장 읽고 기억할 거 세 가지:

  1. 바뀌는 부분을 캡슐화하라 — 달라지는 부분을 찾아서 분리하는 게 모든 디자인 패턴의 출발점이야. 전략 패턴은 이 원칙의 첫 번째 적용이지.
  2. 상속보다 구성을 활용하라 — "has-a" 관계가 "is-a"보다 유연하고, 런타임에도 행동을 바꿀 수 있어.
  3. 느슨한 결합이 설계의 핵심이야 — 옵저버 패턴처럼 인터페이스만 알면 되는 구조를 만들면, 독립적으로 변경하고 확장할 수 있어.