본문 바로가기
OOP/<헤드 퍼스트 디자인 패턴>, 에릭 프리먼 외

Strategy

by 민휘 2023. 2. 24.

핵심 의도

전략 패턴은 알고리즘군을 정의하고 캡슐화해서 각각의 알고리즘군을 수정해 쓸 수 있게 한다. 전략 패턴을 사용하면 클라이언트로부터 알고리즘을 분리해서 독립적으로 변경할 수 있다.

 

적용 상황

어떤 객체의 행동에 대해서, 구체적인 구현 방법을 실행 중에 변경하고 싶을 때 사용한다. 예를 들어 계산기의 덧셈 기능에 대해 일반 계산기에 대해서는 누적합을 구하는 방식으로 덧셈을 하도록 하고, 공학 계산기에 대해서는 가우스 합을 구하는 방식으로 덧셈을 하도록 하고 싶을 때 사용할 수 있다.

 

솔루션의 구조와 각 요소의 역할

객체에게 책임을 분할하기

어떤 행동을 할 수 있는 Context 객체가 존재한다. 이 객체는 각 행동에 대해 클라이언트가 원하는 방식으로 행동할 수 있어야 한다. 그러려면 클라이언트가 원하는 방식으로 행동하는 책임을 가지는 구상 전략 클래스가 필요하다. 구상 클래스는 실행 중에 클라이언트의 요청에 다라 다르게 주입되므로 Strategy 인터페이스로 감싸야 한다.

 

구현 포인트

상속보다는 구성을 사용한다

행동을 인터페이스로 분리할 필요 없이, 행동을 정의하는 슈퍼 객체를 상속받아 오버라이딩하여 사용할 수도 있다. 하지만 상속을 활용하는 방법은 객체를 새로 만들 때마다 메소드를 하나씩 살펴보고 오버라이딩해주어야 한다. 만약 실수로 오버라이딩하지 않으면 우리가 원하는대로 동작하지 않는다.

 

바뀌는 부분은 캡슐화한다

전략 패턴을 적용할만한 상황에서 문제가 되는 부분은 행동의 구현 방식이 객체에 따라 달라진다는 부분이다. 바뀌는 부분은 따로 뽑아서 캡슐화한다. 그러면 나중에 바뀌지 않는 부분에는 영향을 미치지 않고 그 부분만 고치거나 확장할 수 있게 된다.

 

클라이언트가 구체적인 전략 클래스를 지정한다

전략 패턴에서 Context 객체는 Strategy 인터페이스를 멤버 필드로 가지며, Context 생성자에서 클라이언트로부터 원하는 행동 구상 클래스를 주입받는다.

 

적용 예시

 

요구사항

오리는 날거나 울 수 있다. 물오리는 꿱꿱 울고 날 수 있다. 고무 오리는 삑삑 울고 날 수 없다. 가짜 오리는 날 수 없으며 울 수도 없다. 앞으로 추가될 수 있는 오리 객체는 무궁무진하게 많다. 날거나 우는 방법이 다른 오리를 생성하는 유연한 코드를 작성해보자.

 

설계

Context인 Duck는 나는 행동과 우는 행동을 가진다. 나는 행동은 FlyBehavior라는 공통 인터페이스를 가지며, 날개로 나는 방식과 날지 못하는 방식의 행동을 구상 클래스로 구현한다. 우는 행동은 QuackBehavior라는 공통 인터페이스를 가지며, 꽥꽥 거리며 울거나 삑삑 거리며 울거나 울지 못하는 방식의 행동을 구상 클래스로 구현한다. Duck의 구상 클래스는 우는 행동과 나는 행동의 구상 클래스를 오리를 생성하는 클라이언트로부터 받아 결정한다.

 

코드

Duck

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!");
	}
}

MallardDuck

public class MallardDuck extends Duck {

	public MallardDuck() {
		// 클라이언트가 행동 구현체를 직접 지정함
		quackBehavior = new Quack();
		flyBehavior = new FlyWithWings();

	}

	public void display() {
		System.out.println("I'm a real Mallard duck");
	}
}

RubberDuck

public class RubberDuck extends Duck {
 
	public RubberDuck() {
		flyBehavior = new FlyNoWay();
		//quackBehavior = new Squeak();
		quackBehavior = () -> System.out.println("Squeak");
	}
	
	// 클라이언트로부터 행동 구현체를 주입받음
	public RubberDuck(FlyBehavior flyBehavior, QuackBehavior quackBehavior) {
		this.flyBehavior = flyBehavior;
		this.quackBehavior = quackBehavior; 
	}
 
	public void display() {
		System.out.println("I'm a rubber duckie");
	}
}

FlyBehavior

public interface FlyBehavior {
	public void fly();
}

QuackBehavior

public interface QuackBehavior {
	public void quack();
}

Test

public class MiniDuckSimulator {
 
	public static void main(String[] args) {
 
		MallardDuck	mallard = new MallardDuck();
		FlyBehavior cantFly = () -> System.out.println("I can't fly");
		QuackBehavior squeak = () -> System.out.println("Squeak");
		RubberDuck	rubberDuckie = new RubberDuck(cantFly, squeak);
 
		mallard.performQuack();
		rubberDuckie.performQuack();
	}
}

'OOP > <헤드 퍼스트 디자인 패턴>, 에릭 프리먼 외' 카테고리의 다른 글

Singleton  (0) 2023.02.24
Simple Factory, Factory Method, Abstract Factory  (0) 2023.02.24
Decorator  (0) 2023.02.24
Observer  (0) 2023.02.24
공부 방법 설정  (0) 2023.02.24