이 글은 헤드퍼스트 디자인 패턴(한빛 미디어, 에릭 프리먼 외)를 읽고 정리한 것입니다.
전략 패턴이란?
알고리즘군을 정의하고 캡슐화 하여, 각각의 알고리즘군을 수정해서 쓸 수 있도록 만드는 전략이다. 이를 통해 클라이언트로부터 알고리즘을 분리해서 독립적으로 변경하는 것이 가능하다.
디자인 패턴 책에는 전략 패턴을 사용하는 예시로 Duck 클래스를 생성하는 경우를 보여준다.
가령, Duck 추상 클래스가 있고 오리의 종류에 따라 해당 부모 클래스를 상속 받는 구상 클래스가 있다고 해보자.
위와 같은 상황에서 만약 나무로 된 가짜 오리를 추가해야 한다면 어떻게 될까? quack과 display 메소드를 오버라이딩 해서 새로운 특징을 추가해야 할 것이다. 그러나 이렇게 하면 아래와 같은 문제점이 있다.
애플리케이션에서 달라지는 부분은 나머지 코드에 영향을 주지 않도록 캡슐화 해야 한다. 이를 통해 나중에 바뀌지 않는 부분에는 영향을 주지 않는 선에서 바뀌는 부분만 고치거나 확장하는 것이 가능하다.
따라서, 구성을 이용하는 방법을 통해 위의 예시에서 오리의 행동은 상속 받지 않고 오리의 행동 인터페이스를 통해 구성한 행동 객체를 부여 받도록 해야 한다.
-> 즉, "A는 B이다"가 아니라 "A에 B가 있다"가 되도록 하는 것이다.
유의할 점
아래의 인터페이스 각각에 대해 구체적인 행동을 구현하기 위한 클래스를 생성할 수 있다.
FlyBehavior
QuackBehavior
위의 방법과 같이 메소드를 사용하지 않고 다른 클래스에 위임하는 방법을 통해 다른 형식의 객체에서도 나는 행동과 꽥꽥 거리는 행동을 재사용할 수 있다.
오리의 특징을 정의할 Duck 클래스(abstract)를 생성한다.
Duck
public abstract class Duck {
QuackBehavior quackBehavior;
public void performQuack() {
quackBehavior.quack();
} // performQuack 함수는 quackBehavior 인터페이스에 선언된 quack() 함수를 실행한다.
}
- swim()위에서 정의한 Duck 추상 클래스를 이용해 물오리의 특징을 정의할 MallardDuck 클래스를 생성할 수 있다.
public class MallardDuck extends Duck {
public MallardDuck() {
quackBehavior = new Quack();
flyBehavior = new FlyWithWings(); // 물오리는 날 수 있기 때문에 FlyWithWings 객체를 생성한다.
}
public void display() {
System.out.println("물오리입니다");
}
}
먼저, 전략 패턴을 살펴보면서 아래와 같은 객체 지향 원칙이 그 안에 녹아 있음을 확인할 수 있었다.
객체 지향 원칙
위에서 살펴본 오리의 나는 행동/ 꽥꽥 거리는 행동은 각 행동 집합에 대한 알고리즘군으로 생각할 수 있다. 각 행동은 또한 바뀔 수 있기 때문에 캡슐화 되어 있는 구조이다.
디자인 패턴 책에 따르면, 이처럼 알고리즘군을 이용하는 또 다른 방식으로는 세금 계산 방식을 구현하는 클래스가 있다고 한다. 이밖에도 우리 앱에서 이 패턴을 적용해 보기 위해서는 가령 장학금을 주는 방식이 여러가지라 가정했을 때 사용해 볼 수 있을 것 같다. 예를 들어 장학금을 현금으로 주는 방식, 상품권을 주는 방식, 특정 상품을 주는 방식 등으로 다양하다고 한다면 이를 알고리즘군으로 생각하고 전략 패턴을 적용해 볼 수도 있겠다.