포스트

[Behavioral Pattern] 전략 패턴 (Strategy Pattern)

Design Pattern / Behavioral Pattern

전략 패턴의 정의와 해당 디자인 패턴의 예제 코드를 통한 이해 및 설명 정리

view

개념

  • 실행(런타일) 중에 알고리즘 전략을 선택하여 객체 동작을 실시간으로 바뀌도록 할 수 있게 하는 행위 디자인 패턴
  • 여기서 ‘전략’이란 일종의 알고리즘이 될 수도 있으며, 기능이나 동작이 될 수도 있는 특정한 목표를 수행하기 위한 행동 계획을 말함

  • 즉, 어떤 일을 수행하는 알고리즘이 여러가지일 때, 동작들을 미리 전략으로 정의함으로써 손쉽게 전략을 교체할 수 있는, 알고리즘 변형이 빈번하게 필요한 경우에 적합한 패턴

패턴 구조

strategy

  • ConcreteStrategy1~3 (전략 알고리즘 객체들)

    • 알고리즘, 행위, 동작을 객체로 정의한 구현체
  • Strategy (전략 인터페이스)

    • 모든 전략 구현체에 대한 공용 인터페이스
  • Context (컨텍스트)

    • 알고리즘을 실행해야 할 때마다 해당 알고리즘과 연결된 전략 객체의 메소드를 호출
  • Client (클라이언트)

    • 특정 전략 객체를 컨텍스트에 전달 함으로써 전략을 등록하거나 변경하여 전략 알고리즘을 실행한 결과를 누림

GoF에서의 전략 패턴의 정의

  1. 동일 계열의 알고리즘군을 정의하고

    → 전략 구현체로 정의

  2. 각각의 알고리즘을 캡슐화하여

    → 인터페이스로 추상화

  3. 이들을 상호 교환이 가능하도록 만들고,

    → 합성(composition)으로 구성

  4. 알고리즘을 사용하는 클라이언트와 상관없이 독립적으로

    → 컨텍스트 객체 수정 없이

  5. 알고리즘을 다양하게 변경할 수 있게 함

    → 메소드를 통해 전략 객체를 실시간으로 변경함으로써 전략을 변경

예제 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// Run | Walk 전략 (추상화된 알고리즘)
interface MoveStrategy {
  move(): void;
}

class Walk implements MoveStrategy {
  public move(): void {
    console.log("걸어서 배달합니다.");
  }
}

class Run implements MoveStrategy {
  public move(): void {
    console.log("뛰어서 배달합니다.");
  }
}

// 한국어 | 일본어 번역 전략 (추상화된 알고리즘)
interface TranslateStrategy {
  translate(): void;
}

class Korean implements TranslateStrategy {
  public translate(): void {
    console.log("한국어로 번역합니다.");
  }
}

class Japanese implements TranslateStrategy {
  public translate(): void {
    console.log("일본어로 번역합니다.");
  }
}

// Context (전략 등록 / 실행)
class Robot {
  moveStrategy: MoveStrategy;
  translateStrategy: TranslateStrategy;

  constructor(
    moveStrategy: MoveStrategy,
    translateStrategy: TranslateStrategy
  ) {
    this.moveStrategy = moveStrategy;
    this.translateStrategy = translateStrategy;
  }

  move(): void {
    this.moveStrategy.move();
  }

  translate(): void {
    this.translateStrategy.translate();
  }

  setMove(moveStrategy: MoveStrategy) {
    this.moveStrategy = moveStrategy;
  }

  setTranslate(translateStrategy: TranslateStrategy) {
    this.translateStrategy = translateStrategy;
  }
}

// Client (전략 교체 / 전략 실행한 결과를 얻음)
class User {
  public static main(_args?: string[]): void {
    const robot: Robot = new Robot(new Walk(), new Korean());
    robot.move(); // 걸어서 배달합니다.
    robot.translate(); // 한국어로 번역합니다.

    console.log("");

    // 로봇의 전략(기능)을 Run과 일본어 번역으로 변경
    robot.setMove(new Run());
    robot.setTranslate(new Japanese());

    robot.move(); // 뛰어서 배달합니다.
    robot.translate(); // 일본어로 번역합니다.
  }
}

User.main();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
// 전략 (추상화된 알고리즘)
interface PaymentStrategy {
  pay(amount: number): void;
}

class KakaoCardStrategy implements PaymentStrategy {
  private name: string;
  private cardNumber: number;
  private cvv: number;
  private dateOfExpire: string;

  constructor(
    name: string,
    cardNumber: number,
    cvv: number,
    dateOfExpire: string
  ) {
    this.name = name;
    this.cardNumber = cardNumber;
    this.cvv = cvv;
    this.dateOfExpire = dateOfExpire;
  }

  public pay(amount: number): void {
    console.log(`${amount}₩ paid using Kakao Card`);
  }
}

class LunaCardStrategy implements PaymentStrategy {
  private email: string;
  private password: string;

  constructor(email: string, password: string) {
    this.email = email;
    this.password = password;
  }

  public pay(amount: number): void {
    console.log(`${amount}₩ paid using Luna Card`);
  }
}

class Item {
  public name: string;
  public price: number;

  constructor(name: string, price: number) {
    this.name = name;
    this.price = price;
  }
}

// Context (전략 등록 / 실행)
class ShoppingCart {
  items: Item[];

  constructor() {
    this.items = new Array<Item>();
  }

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

  // 전략은 매개변수로 받아서 바로 전략을 실행
  public pay(paymentMethod: PaymentStrategy): void {
    let amount: number = 0;
    for (let item of this.items) {
      amount += item.price;
    }

    paymentMethod.pay(amount);
  }
}

// Client (전략 제공 / 설정)
class PayUser {
  public static main(_args?: string[]): void {
    // 쇼핑카트 전략 Context 등록
    const cart: ShoppingCart = new ShoppingCart();

    // 쇼핑 물품
    const A: Item = new Item("맥북 에어", 10000);
    const B: Item = new Item("맥북 프로", 30000);

    cart.addItem(A);
    cart.addItem(B);

    // Luna Card로 결제 전략 실행
    cart.pay(new LunaCardStrategy("han1210_36@naver.com", "password1234"));

    // Kakao Card로 결제 전략 실행
    cart.pay(new KakaoCardStrategy("HyungJinHan", 123456789, 123, "12/10"));
  }
}

PayUser.main();

참고한 출처 사이트

Refactoring GURU

Inpa Dev Blog (디자인 패턴)

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.