"헤드 퍼스트 디자인 패턴(개정판)"을 읽고 정리한 내용입니다.
1. 디자인 패턴 소개와 전략 패턴
자바의 인터페이스는 구현된 코드(Body)가 없으므로 코드를 재사용할 수 없는 문제점이 존재한다. 즉, 한 가지 행동을 바꿀 때마다 그 행동이 정의되어 있는 서로 다른 서브 클래스들을 모두 찾아서 코드를 일일히 수정해야 하는 번거로움이 존재한다. 이 문제를 해결하기 위해 전략 패턴(Strategy 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) 동적으로 행동을 바꿀 수도 있다.
전략 패턴(Strategy Pattern)
전략 패턴은 알고리즘군을 정의하고, 각각의 알고리즘을 캡슐화해서 상호 교환(알고리즘을 선택해서 사용할 수 있다는 말)할 수 있도록 한다. 전략 패턴을 사용하면 알고리즘을 사용하는 클라이언트로부터 독립적으로 알고리즘을 변경할 수 있다.(즉, 클라이언트는 알고리즘의 구현을 몰라도 알고리즘을 쉽게 교체할 수 있다.)
예시
public interface FlyBehavior {
public void fly();
}
public class FlyWithWings implements FlyBehavior {
@Override
public void fly() {
System.out.println("날고 있습니다.");
}
}
public class FlyNoWay implements FlyBehavior {
@Override
public void fly() {
System.out.println("날 수 없어요.");
}
}
public interface QuackBehavior {
void quack();
}
public class Quack implements QuackBehavior {
@Override
public void quack() {
System.out.println("꽥꽥!");
}
}
public class Squeak implements QuackBehavior {
@Override
public void quack() {
System.out.println("삑삑!");
}
}
public class MuteQuack implements QuackBehavior {
@Override
public void quack() {
System.out.println("울 수 없어요!");
}
}
public abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public Duck() {
}
public void setFlyBehavior(FlyBehavior fb) {
flyBehavior = fb;
}
public void setQuackBehavior(QuackBehavior qb) {
quackBehavior = qb;
}
abstract void display();
public void performFly() {
flyBehavior.fly();
}
public void performQuack() {
quackBehavior.quack();
}
public void swim() {
System.out.println("All ducks float, even decoys!");
}
}
public class MallardDuck extends Duck {
public MallardDuck() {
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
}
@Override
void display() {
System.out.println("I'm a mallard duck");
}
}
public class ModelDuck extends Duck {
public ModelDuck() {
flyBehavior = new FlyNoWay();
quackBehavior = new Quack();
}
public void display() {
System.out.println("I'm a model duck");
}
}
public class MiniDuckSimulator {
public static void main(String[] args) {
Duck mallard = new MallardDuck();
mallard.performQuack();
mallard.performFly();
Duck model = new ModelDuck();
model.setFlyBehavior(new FlyWithWings());
model.setQuackBehavior(new MuteQuack());
model.performQuack();
model.performFly();
}
}
// Output
꽥꽥!
날고 있습니다.
울 수 없어요!
날고 있습니다.