3 분 소요

전략 패턴(Strategy Pattern)이란?

전략 패턴은 실행(런타임) 중에 알고리즘 전략을 선택하여 객체 동작을 실시간으로 바뀌도록 할 수 있게 하는 행위 디자인 패턴입니다.

전략이란 알고리즘, 기능, 동작 등 특정 목표를 수행하기 위한 행동 계획입니다.

구조

strategy

  • Strategy : 전략 구현체에 대한 인터페이스
  • ConcreteStrategy : 알고리즘, 행위, 동작을 정의한 구현체
  • Context : 전략을 등록, 실행합니다. 전략을 실행해야 할때 등록된 전략의 메소드를 호출하도록 위임합니다.
  • Client : 특정 전략을 Context에 등록, 변경하여 전략을 실행한 결과를 얻습니다.

참고

프로그래밍에서의 컨텍스트(Context) 란 콘텐츠(Contetns)를 담는 그 무엇인가를 뜻하며, 어떤 객체를 핸들링 하기 위한 접근 수단입니다. 즉, 물컵에 물이 담겨있으면 물은 콘텐츠가 되고, 물컵은 컨텍스트가 되며, 물을 핸들링 하기 위한 접근 수단이 됩니다.

구현

public interface IStrategy {
	void doSomething();
}

public class ConcreteStrategyA implements IStrategy{
	@Override
	public void doSomething() {
		System.out.println("Strategy A");
	}
}

public class ConcreteStrategyB implements IStrategy{
	@Override
	public void doSomething() {
		System.out.println("Strategy B");
	}
}

public class Context {
	private IStrategy strategy;

	public void setStrategy(IStrategy strategy) {
		this.strategy = strategy;
	}

	public void doSomething() {
        // 공통 로직
        
		strategy.doSomething();
	}
}

public class Client {
	public static void main(String[] args) {
		Context context = new Context();

		context.setStrategy(new ConcreteStrategyA());
		context.doSomething();

		context.setStrategy(new ConcreteStrategyB());
		context.doSomething();
	}
}

특징

패턴 사용 시기

  • 다양한 전략 알고리즘이 필요할 때 클래스화를 통해 관리합니다.
  • 알고리즘 동작이 런타임에 실시간으로 교체 되어야 할 때 사용합니다.

장점

  • 공통 로직이 Context에 있기 때문에 구현체들에 대한 영향도가 적습니다.
  • 구체적인 클래스가 아닌 Strategy라는 인터페이스를 의존하기때문에 변경이 쉽고 OCP를 지킬 수 있습니다.

단점

  • 알고리즘이 증가할때마다 구현 클래스가 증가하여 관리할 대상이 많아집니다.

예제1

@Setter
public class Robot {
	private MoveStrategy moveStrategy;
	private TranslateStrategy translateStrategy;

	public Robot(MoveStrategy moveStrategy, TranslateStrategy translateStrategy) {
		this.moveStrategy = moveStrategy;
		this.translateStrategy = translateStrategy;
	}

	public void move() {
		moveStrategy.move();
	}

	public void translate() {
		translateStrategy.translate();
	}
}

public interface MoveStrategy {
	void move();
}

public class Walk implements MoveStrategy {
	@Override
	public void move() {
		System.out.println("걷기");
	}
}

public class Run implements MoveStrategy {
	@Override
	public void move() {
		System.out.println("뛰기");
	}
}

public interface TranslateStrategy {
	void translate();
}

public class Korean implements TranslateStrategy {
	@Override
	public void translate() {
		System.out.println("한국어 번역");
	}
}

public class Japanese implements TranslateStrategy {
	@Override
	public void translate() {
		System.out.println("일본어 번역");
	}
}

public class Client {
	public static void main(String[] args) {
		Robot robot = new Robot(new Walk(), new Korean());
		robot.move();
		robot.translate();

		robot.setMoveStrategy(new Run());
		robot.setTranslateStrategy(new Japanese());

		robot.move();
		robot.translate();
	}
}

예제2

기존 Strategy 패턴과 조금은 다른 형태로 Strategy 를 필드로 합성(composition)하지 않고 pay() 메소드의 매개변수로 받는 형태입니다.

public class ShoppingCart {
	private List<Item> items = new ArrayList<>();

	public void addItem(Item item) {
		this.items.add(item);
	}

	public void pay(PaymentStrategy paymentMethod) {
		int amount = 0;
		for (Item item : items) {
			amount += item.getPrice();
		}
		paymentMethod.pay(amount);
	}
}

@Getter
public class Item {
	private String name;
	private int price;

	public Item(String name, int price) {
		this.name = name;
		this.price = price;
	}
}

public interface PaymentStrategy {
	void pay(int amount);
}

public class KakaoCardStrategy implements PaymentStrategy {
	private String name;
	private String cardNumber;
	private String cvv;
	private String dateOfExpiry;

	public KakaoCardStrategy(String name, String cardNumber, String cvv, String dateOfExpiry) {
		this.name = name;
		this.cardNumber = cardNumber;
		this.cvv = cvv;
		this.dateOfExpiry = dateOfExpiry;
	}

	@Override
	public void pay(int amount) {
		System.out.println(amount + "원 paid by kakao card");
	}
}

public class LunaCardStrategy implements PaymentStrategy {
	private String emailId;
	private String password;

	public LunaCardStrategy(String email, String pwd) {
		this.emailId = email;
		this.password = pwd;
	}

	@Override
	public void pay(int amount) {
		System.out.println(amount + "원 paid by luna card");
	}
}

public class User {
	public static void main(String[] args) {
		ShoppingCart cart = new ShoppingCart();

		// 쇼핑 물품
		Item A = new Item("맥북 프로", 10000);
		Item B = new Item("플레이스테이션", 30000);
		cart.addItem(A);
		cart.addItem(B);

		// LUNACard로 결제 전략 실행
		cart.pay(new LunaCardStrategy("kundol@example.com", "pukubababo")); // 4000원 paid using LUNACard.

		// KAKAOBank로 결제 전략 실행
		cart.pay(new KakaoCardStrategy("Ju hongchul", "123456789", "123", "12/01")); // 4000원 paid using KAKAOCard.
	}
}

이와 같은 형태를 Tempate Callback 패턴이라고도 부릅니다.

reference

참고1

댓글남기기