Home 행위패턴 - 전략패턴과 상태 패턴
Post
Cancel

행위패턴 - 전략패턴과 상태 패턴

Strategy Pattern & State Pattern 🧙‍♂️

디자인 패턴이란?

디자인 패턴이란?

  • 디자인 패턴은 소프트웨어 공학의 소프트웨어 설계에서 공통으로 발생하는 문제를 자주 쓰이는 설계 방법을 정리한 패턴이다.
  • 디자인 패턴을 참고하여 개발하면 효율성과 유지보수성, 운용성이 높아지며, 프로그램 최적화가 된다고 한다.  

디자인 패턴을 목적과 범위로 나눌수 있다

구분유형설명
 생성객체 인스턴스 생성에 관여, 클래스 정의와 객체 생성 방식을 구조화, 캡슐화를 수행
목적구조더 큰 구조 형성 목적으로 클래스나 객체의 조합을 다루는 패턴
 행위클래스나 객체들이 상호작용하는 방법과 역할 분담을 다루는 패턴
범위클래스클래스간 관련성(상속), 컴파일 시 정적으로 결정
 객체객체 간 관련성을 다루는 패턴, 런타임 시 동적으로 결정

  

전략 패턴은 인스턴스를 생성하고 난 후, 상태가 거의 바뀌지 않는 경우에 사용
상태 패턴은 인스턴스를 생성하고 난 후, 상태가 빈번하게 바뀌는 경우에 사용

전략 패턴이란? strategy pattern

정책패턴이라고 하며, 객체의 행위를 바꾸고 싶을떄 직접 수정하지 않고 ‘캡슐화한 알고리즘’을 컨텍스트 안에서 바꿔주면서 상호간의 교체가 가능하도록 만드는 패턴이다.
결제시스템에서 카카오페이든 네이버페이든 전략만 바꿔서 결제를 진행하도록 해주는 것이 전략 패턴이다.

인스턴스 오리가 태어날떄 꼭 한가지는 가지고 태어난다고 가정해보자.

  1. 날 수있는 오리
  2. 날 수없는 오리

한번 인스턴스가 생성(태어나면) 살아가는 동안 날지 못하는 오리가 갑자기 날 수는 없는 것이다.
즉, 상태가 바뀌지 않는다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface Fly{
    public void fly();
}
class canfly implements Duck{
   @Override
   public void fly(){
    System.out.print("날수 있다.");
   } 
}
class cannotfly implements Duck{
   @Override
   public void fly(){
    System.out.print("날수 없다.");
   } 
}

이런식으로 코드를 짜면 수정이나 기능을 추가시에 메서드의 중복문제가 발생과 OCP를 위반하게 된다.

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
abstract class Duck{
    private String name;
    private FlyingStrategy flyingStrategy;

    public Duck(String name) {
        this.name = name;
    }

    public void fly() {
        System.out.printf(name +"는 ");
        flyingStrategy.fly();
    }
    public void setFlyingStrategy(FlyingStrategy flyingStrategy) {
        this.flyingStrategy = flyingStrategy;
    }
}

class blackDuck extends Duck{
    public blackDuck(String name){
        super(name);
    }
}

class whiteDuck extends Duck{
    public whiteDuck(String name){
        super(name);
    }
}
//fly 인터페이스
interface FlyingStrategy{
    public void fly();
}

//fly 구현체
class canFly implements FlyingStrategy {
    @Override
    public void fly() {
        System.out.println("can fly");
    }
}
//fly 구현체
class cannotFly implements FlyingStrategy {
    @Override
    public void fly() {
        System.out.println("cannot fly");
    }
}


public class Client {
    public static void main(String[] args) {
        blackDuck blackDuck = new blackDuck("검정오리");
        whiteDuck whiteDuck = new whiteDuck("흰오리");
        blackDuck.setFlyingStrategy(new canFly());
        whiteDuck.setFlyingStrategy(new cannotFly());
        blackDuck.fly();
        whiteDuck.fly();
    }
}
//검정오리는 can fly
//흰오리는 cannot fly

img

상태 패턴이란? state pattern

상태 객체에 일련의 행동이 캡슐화가 된다. 즉, 실행중인 객체가 여러 상태 객체중 하나인 객체에게 모든 행동을 맞기게 된다.

아래와 같이 자판기기계가 있다고 가정해보자..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1.  경우에는 기계가 HasMoney() 상태 있을때 insert_money() 호출되면서 Sold() 상태로 전환된다.  

                                  ==> 돈이 부족할때 NoMoney();
                                  ==> 돈이 있을때 HasMoney();
-insert_money()-> Vending Machine =[현재 상태]=> 거스름돈이 없을떄 NoChange();
                                  ==>  팔렸을때 SoldOut();
                                  ==> 팔릴떄 Sold();

2. 현재 상태가 Sold 메서드로 전환되면서 음료를 반출한다.

                                  ==> 돈이 부족할때 NoMoney();
                                  ==> 돈이 있을때 HasMoney();
                                  ==> 거스름돈이 없을떄 NoChange();
                                  ==>  팔렸을때 SoldOut();
Vending Machine=[현재 상태]+ dispense()=> 팔릴떄 Sold();

3. 그후 음료가 있으면 NoMoney(); , 거스름돈이없으면 NoChange(), 매진  SoldOut()으로 이동한다.

이처럼 상태를 빈번히 바뀌는 경우 상태패턴을 사용한다. 코드를 간단하게 짜보겠다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
변경전 로직
public class VendingMachine {
    public static enum State {NoMoney, HasMoney, ...}
    private State state = State.NoMoney;
    public void changeState(State state){
       this.state=state;
   }
    public void insert_money(int money) {
        switch (state) {
            case NoMoney:
            case HasMoney:
            case Nochange:
            case SoldOut:
            case Sold:
        }
    }
}

벌써부터 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
interface State{
    public void money(int money, VendingMachine machine);
}
class NoMoney implements State{
    @Override
    public void money(int money, VendingMachine machine) {
        ...
    }
}

class HasMoney implements State{
    @Override
    public void money(int money, VendingMachine machine){
        machine.changeState(new Sold());
    }
}

class Sold implements State{
    @Override
    public void money(int money, VendingMachine machine){
        machine.changeState(new NoMoney());
    }
}


public class VendingMachine {
    private State state;
    public VendingMachine() {
        state = new NoMoney();
    }
    public void insert_money(int money) {
        state.money(money, this); // 핵심포인트: 구현위임
    }
    public void changeState(State state){
        this.state = state;
    }
}

공통점

차이점은 위에 말한것처럼 state는 능동적으로 변하고 strategy는 수동적으로 변하는 성질이 있다.
공통점은 실행중인 클래스의 영향을 받지않고 유연한 변환이 가능한 점이다.

결론

전략 패턴: 생성 시, 비슷하면서 조금씩 다른 행동을 묶고 싶을때 사용한다.
상태 패턴: if,else 조건문이 너무 많이 들어가 지저분해보일 때 사용한다.

출처

  • Head First 디자인패턴
  • 면접을 위한 CS 전공지식노트
This post is licensed under CC BY 4.0 by the author.