문제를 명확하게 파악하기
- 상속이 그리 성공적인 해결책이 아님
- 서브클래스마다 오리의 행동이 바뀔 수 있는데도 모든 서브클래스에서 한 가지 행동만 사용하도록 하는 것은 그리 올바르지 못함
- 한 가지 행동을 바꿀 때 마다 그 행동이 정의되어 있는 서로 다른 서브클래스를 전부 찾아서 코드를 일일이 고쳐야하고, 그 과정에서 새로운 버그가 생길 가능성도 있음
- 디자인 원칙: 애플리케이션에서 달라지는 부분을 찾아내고, 달라지지 않는 부분과 분리한다.
- 달라지는 부분을 찾아서 나머지 코드에 영향을 주지 않도록 ‘캡슐화’합니다. 그러면 코드를 변경하는 과정에서 의도치 않게 발생하는 일을 줄이면서 시스템의 유연성을 향상시킬 수 있습니다.
- 이 개념은 다른 모든 디자인 패턴의 기반을 이루는 원칙
- 모든 패턴은 ‘시스템의 일부분을 다른 부분과 독립적으로 변화시킬 수 있는’ 방법을 제공하기 때문
바뀌는 부분과 그렇지 않은 부분 분리하기
- fly()와 quack() 문제를 제외하면 Duck 클래스는 잘 작동하고 있으며, 나머지 부분은 자주 달라지거나 바뀌지 않음
- ‘변화하는 부분과 그대로 있는 부분’을 분리하려면 (Duck 클래스와는 완전히 별개로) 2개의 클래스 집합(set)을 만들어야 함. 하나는 나는 것과 관련된 부분, 다른 하나는 꽥꽥거리는 것과 관련된 부분.
- 각 클래스 집합에는 각각의 행동을 구현한 것을 전부 집어넣음
오리의 행동을 디자인하는 방법
- 목표
- 최대한 유연하게
- Duck의 인스턴스에 행동을 할당할 수 있도록
- 오리의 행동을 동적으로 바꿀 수 있다면 더 좋음
- 디자인 원칙: 구현보다는 인터페이스에 맞춰서 프로그래밍한다.
- 이제부터 Duck의 행동은 (특정 행동 인터페이스를 구현한) 별도의 클래스 안에 들어있습니다. 그러면 Duck 클래스에서는 그 행동을 구체적으로 구현할 필요가 없습니다.
- “인터페이스에 맞춰서 프로그래밍한다”라는 말은 사실 “상위 형식에 맞춰서 프로그래밍한다”라는 말입니다.
- 핵심은 실제 실행 시에 쓰이는 객체가 코드에 고정되지 않도록 상위 형식(suptertype)에 맞춰 프로그래밍해서 다형성을 활용해야 한다는 점에 있습니다.
- 즉, “상위 형식에 맞춰서 프로그래밍하라”는 원칙은 “변수를 선언할 때 보통 추상 클래스나 인터페이스 같은 상위 형식으로 선언해야 한다. 객체를 변수에 대입할 때 상위 형식을 구체적으로 구현한 형식이라면 어떤 객체든 넣을 수 있기 때문이다. 그러면 변수를 선언하는 클래스에서 실제 객체의 형식을 몰라도 된다”라는 뜻으로 생각하면 됩니다.
오리의 행동을 구현하는 방법
- 여기에서는 FlyBehavior와 QuackBehavior라는 2개의 인터페이스를 사용합니다. 그리고 구체적인 행동을 구현하는 클래스들이 있습니다.