
"헤드 퍼스트 디자인 패턴(개정판)"을 읽고 정리한 내용입니다.
8. 알고리즘 캡슐화하기
템플릿 메소드 패턴
템플릿 메소드 패턴(Template Method Pattern)은 메소드에서 알고리즘의 뼈대를 정의하고, 서브클래스에게 알고리즘의 몇몇 단계를 맡긴다. 템플릿 메소드 패턴은 알고리즘의 구조를 변경하는 것 없이 알고리즘의 특정 단계를 서브클래스가 재정의하도록 한다.
디자인 원칙
- 애플리케이션에서 달라지는 부분을 식별하고 불변으로부터 그것들을 분리해라. (Identify the aspects of your application that vary and separate them from what stays the same.)
- 달라지는 부분을 찾아서 나머지 코드에 영향을 주지 않도록 캡슐화(encapsulate)한다. 그러면 나중에 바뀌지 않는 부분에는 영향을 미치지 않고 그 부분만 고치거나 확장할 수 있다.
- 구현이 아니라 인터페이스에 프로그래밍해라. (Program to an interface, not an implementation.)
- 특정 행동만을 목적으로 하는 클래스의 집합을 만들고, 이 클래스에서 구현한다. 그러면 슈퍼 클래스에서 구체적으로 구현하거나 서브 클래스 자체에서 별도로 구현하지 않아서 특정 구현에 의존하지 않는다.
- 상속보다는 구성이 더 낫다. (Favor composition over inheritance.)
- 여기서 구성(composition)은 행동을 상속받는 것 대신에, 올바른 행동 객체로 구성되어 행동을 부여받는 것을 말한다.
- 구성을 사용하면 유연성을 향상 시킬 수 있다. 알고리즘군을 별도의 클래스로 캡슐화 할 수 있을 뿐만 아니라, 구성 요소로 사용하는 객체에서 올바르게 행동 인터페이스를 구현하기만 한다면 실행 중에(runtime) 동적으로 행동을 바꿀 수도 있다.
- 상호작용하는 객체들 사이에는 느슨한 결합을 사용하도록 노력해라. (Strive for loosely coupled designs between objects that interact.)
- 느슨한 결합(Loose Coupling)은 객체들 사이의 종속성이 적다는 뜻이다. 즉, 객체들 사이의 유연성이 좋아진다. 대표적인 예로 "옵저버 패턴"이 있다.
- 주제(Subject)는 옵저버가 특정 인터페이스(Observer 인터페이스)를 구현한다는 사실만 알고있다. 즉, 주제는 옵저버가 무엇을 하는지 알 필요가 없다.
- 옵저버는 언제든지 새로 추가할 수 있다. (제거도 마찬가지이다.)
- 새로운 타입의 옵저버를 추가할 때도 주제를 수정할 필요가 없다.
- 주제와 옵저버는 서로 독립적으로 재사용할 수 있다.
- 주제나 옵저버가 달라져도 서로에게 영향을 미치지 않는다.
- 느슨한 결합(Loose Coupling)은 객체들 사이의 종속성이 적다는 뜻이다. 즉, 객체들 사이의 유연성이 좋아진다. 대표적인 예로 "옵저버 패턴"이 있다.
- 클래스는 확장에는 열려있어야 하지만, 변경에는 닫혀 있어야 한다. (Classes should be open for extension, but closed for modification.)
- 이 원칙은 OCP(Open-Closed Principle, 개방-폐쇄 원칙)이다.
- 기존 코드를 변경하지 않고 확장으로 새로운 행동을 추가해야 한다는 말이다.
- 추상화에 의존해라. 구상 클래스에 의존하지 마라.(Depend upon abstractions. Do not depend upon concrete classes.)
- 이 원칙은 DIP(Dependency Inversion Principle, 의존성 뒤집기 원칙)이다.
- 이 원칙을 지키기 위한 가이드 라인은 아래와 같다.
- 변수에 구상 클래스의 레퍼런스를 저장하지 마라.
- 구상 클래스에서 유도된 클래스를 만들지 마라.
- 베이스 클래스에 이미 구현되어 있는 메소드를 오버라이드하지 마라.
- 가까운 친구에게만 말해라. (Talk only to your immediate friends.)
- 이 원칙은 디미터의 법칙(Law of Demeter), 최소 지식 원칙(Principle of Least Knowledge)이다.
- 객체 간의 상호작용을 최소화하여 결합도를 낮춰야 한다는 말로, 체인 호출(점(dot)으로 계속해서 호출하는 것)을 피하라는 것이다.
- 이 원칙을 지키기 위한 가이드 라인은 아래와 같다.
- 객체 자신에 대해서 상호작용.
- 메소드에 매개변수로 전달된 객체에 대해서 상호작용.
- 메소드에서 생성하거나 인스턴스를 만든 객체에 대해서 상호작용.
- 객체에 속하는 구성 요소에 대해서 상호작용.
- 이 원칙은 디미터의 법칙(Law of Demeter), 최소 지식 원칙(Principle of Least Knowledge)이다.
- 우리에게 연락하지 마라. 우리가 너에게 연락할 것이다. (Don't call us, we'll call you.)
- 이 원칙은 할리우드 원칙(Hollywood Principle)이다.
- 상위 레벨에서 하위 레벨을 호출하지, 하위 레벨에서 상위 레벨을 호출하면 안된다는 말이다.
템플릿 메소드 패턴 예시
public abstract class CaffeineBeverage {
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
addCondiments();
}
abstract void brew();
abstract void addCondiments();
void boilWater() {
System.out.println("Boiling water");
}
void pourInCup() {
System.out.println("Pouring into cup");
}
}
public class Coffee extends CaffeineBeverage {
public void brew() {
System.out.println("Dripping Coffee through filter");
}
public void addCondiments() {
System.out.println("Adding Sugar and Milk");
}
}
public class Tea extends CaffeineBeverage {
public void brew() {
System.out.println("Steeping the tea");
}
public void addCondiments() {
System.out.println("Adding Lemon");
}
}