전략 패턴이란?
- 실행중에 알고리즘 전략을 선택하여 객체 동작을 실시간으로 바뀌도록 할 수 있게 하는 패턴이다.
전략 패턴은 OOP의 집합체
GOF의 디자인 패턴 책에서는 전략 패턴을 다음과 같이 정의한다.
- 동일 계열의 알고리즘군을 정의하고 ->
전략 구현체로 정의
- 각각의 알고리즘을 캡슐화하여 ->
인터페이스로 추상화
- 이들을 상호 교환이 가능하도록 만든다. ->
합성(composition)으로 구성
- 알고리즘을 사용하는 클라이언트와 상관없이 독립적으로->
컨텍스트 객체 수정 없이
- 알고리즘을 다양하게 변경할 수 있게 한다. ->
메소드를 통해 전략 객체를 실시간으로 변경함으로써 전략을 변경
구현 예제
BAD CASE
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
class TakeWeapon {
public static final int SWORD = 0;
public static final int SHIELD = 1;
public static final int CROSSBOW = 2;
private int state;
void setWeapon(int state) {
this.state = state;
}
void attack() {
if (state == SWORD) {
System.out.println("칼을 휘두르다");
} else if (state == SHIELD) {
System.out.println("방패로 밀친다");
} else if (state == CROSSBOW) {
System.out.println("석궁을 발사하다");
}
}
}
class User {
public static void main(String[] args) {
// 플레이어 손에 무기 착용 전략을 설정
TakeWeapon hand = new TakeWeapon();
// 플레이어가 검을 들도록 전략 설정
hand.setWeapon(TakeWeapon.SWORD);
hand.attack(); // "칼을 휘두르다"
// 플레이어가 방패를 들도록 전략 설정
hand.setWeapon(TakeWeapon.SHIELD);
hand.attack(); // "방패로 밀친다"
}
}
보기엔 직관적이고 괜찮아 보이지만,
새로운 무기가 추가되어 20개가 넘는상태가 된다고 가정하면. 분기 지옥
에 빠질 수 있다.
GOOD CASE-1
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
// 전략 - 추상화된 알고리즘 (1~3단계)
interface Weapon {
void offensive();
}
class Sword implements Weapon {
@Override
public void offensive() {
System.out.println("칼을 휘두르다");
}
}
class Shield implements Weapon {
@Override
public void offensive() {
System.out.println("방패로 밀친다");
}
}
class CrossBow implements Weapon {
@Override
public void offensive() {
System.out.println("석궁을 발사하다");
}
}
// 컨텍스트 - 전략을 등록하고 실행 (4단계)
class TakeWeaponStrategy {
Weapon wp;
void setWeapon(Weapon wp) {
this.wp = wp;
}
void attack() {
wp.offensive();
}
}
// 클라이언트 - 전략 제공/설정 (5단계)
class User {
public static void main(String[] args) {
// 플레이어 손에 무기 착용 전략을 설정
TakeWeaponStrategy hand = new TakeWeaponStrategy();
// 플레이어가 검을 들도록 전략 설정
hand.setWeapon(new Sword());
hand.attack(); // "칼을 휘두르다"
// 플레이어가 방패를 들도록 전략 변경
hand.setWeapon(new Shield());
hand.attack(); // "방패로 밀친다"
// 플레이어가 석궁을 들도록 전략 변경
hand.setWeapon(new Crossbow());
hand.attack(); // "석궁을 발사하다"
}
}
GOOD-CASE2
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
// 전략 인터페이스
public interface SortingStrategy {
void sort(int[] array);
}
// 전략 클래스 구현
public class BubbleSort implements SortingStrategy {
public void sort(int[] array) {
// 버블 정렬 구현
}
}
public class QuickSort implements SortingStrategy {
public void sort(int[] array) {
// 퀵 정렬 구현
}
}
// 매개체 클래스
public class Sorter {
private SortingStrategy strategy;
public void setStrategy(SortingStrategy strategy) {
this.strategy = strategy;
}
public void executeStrategy(int[] array) {
strategy.sort(array);
}
}
public class Main {
public static void main(String[] args) {
Sorter sorter = new Sorter();
// 버블 정렬 전략 설정
sorter.setStrategy(new BubbleSort());
int[] array1 = {5, 3, 1, 4, 2};
sorter.executeStrategy(array1);
// 퀵 정렬 전략 설정
sorter.setStrategy(new QuickSort());
int[] array2 = {5, 3, 1, 4, 2};
sorter.executeStrategy(array2);
}
}
클라이언트 코드는 매개체 클래스를 통해 각 전략을 사용할 수 있다.
전략 패턴을 사용하면 전략을 유연하게 변경하고 확장할 수 있으며,
클라이언트 코드와 전략의 결합도를 낮출 수 있다.
마치며
- 전략이 늘어날수록 관리해야할 객체의 수가 증가한다.
- 만일 단순한 전략이라면 굳이 해당 패턴을 적용할 필요가 없다.