[Design Pattern] 상태 패턴

State Pattern

상태 패턴은 객체의 내부 상태에 따라 스스로 행동을 변경할 수 있게 허가하는 패턴이다.

활용성

  • 객체의 행동이 상태에 따라 달라질 수 있고 객체의 상태에 따라서 런타임에 행동이 바뀌어야 할 때
  • 어떤 연산에 그 객체의 상태에 따라 달라지는 다중 분기 조건 처리가 너무 많이 들어있을 때

상태 패턴 사용에 따른 결과

상태에 따른 행동 국소화

상태 패턴을 사용하면 임의의 한 상태에 관련된 모든 행동을 하나의 객체로 모을 수 있다. 즉, 서로 다른 상태에 대한 행동을 별도의 객체로 관리할 수 있게 된다. 그렇기 때문에 새로운 상태와 새로운 전이 규칙이 발견되면 새로운 서브 클래스만 정의하면 된다.

상태 객체의 공유

상태는 단지 타입으로만 표현되므로 State 객체는 인스턴스 변수 없이 여러 Context 클래스의 인스턴스로도 객체를 공유할 수 있다.

상태

구조

상태의 구조는 다음과 같다.

State Pattern Diagram

C++ 구현

1
2
3
4
5
6
void StatePattern::StatePattern()
{
CoffeeMachine* coffeeMachine = new CoffeeMachine();
for(int i = 0; i < 20; i++)
coffeeMachine->getCoffee();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
class CoffeeMachine
{
int cup;
MachineStatus* status;
public:
CoffeeMachine();
int getCup();
void useCup();
void setCoffeeStatus(MachineStatus* status);
void getCoffee();
bool isClean();
bool isEmpty();
};
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
class MachineStatus
{
public:
virtual ~MachineStatus() {}
virtual void click(CoffeeMachine* machine) = 0;
};

class CoffeeStatusRun : public MachineStatus
{
public:
~CoffeeStatusRun() {}
void click(CoffeeMachine* coffeeMachine) override;
};

class MachineStatusClean : public MachineStatus
{
public:
~MachineStatusClean() {}
void click(CoffeeMachine* coffeeMachine) override;
};

class MachineStatusEmpty : public MachineStatus
{
public:
~MachineStatusEmpty() {}
void click(CoffeeMachine* coffeeMachine) override;
};
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
CoffeeMachine::CoffeeMachine()
{
this->status = new CoffeeStatusRun();
cup = 15;
}

int CoffeeMachine::getCup()
{
return cup;
}

void CoffeeMachine::useCup()
{
--cup;
}

void CoffeeMachine::setCoffeeStatus(MachineStatus *status) {
delete this->status;
this->status = status;
}

void CoffeeMachine::getCoffee() {
status->click(this);
}

bool CoffeeMachine::isClean() {
return cup % 10 == 0;
}

bool CoffeeMachine::isEmpty() {
return getCup() == 0;
}

void MachineStatusClean::click(CoffeeMachine *coffeeMachine) {
std::cout << "청소중입니다!" << std::endl;
coffeeMachine->setCoffeeStatus(new CoffeeStatusRun());
}

void MachineStatusEmpty::click(CoffeeMachine *coffeeMachine) {
std::cout << "커피가 없습니다!" << std::endl;
}

void CoffeeStatusRun::click(CoffeeMachine *coffeeMachine) {
std::cout << "커피를 뽑습니다." << std::endl;
coffeeMachine->useCup();
std::cout << "남은 커피수 : " << coffeeMachine->getCup() << std::endl;

if (coffeeMachine->isEmpty())
coffeeMachine->setCoffeeStatus(new MachineStatusEmpty());
else if (coffeeMachine->isClean())
coffeeMachine->setCoffeeStatus(new MachineStatusClean());
}

java 구현

1
2
3
4
5
6
7
public class Client {
public static void main(String[] args) {
CoffeeMachine coffeeMachine = new CoffeeMachine();
for(int i = 0; i < 20; i++)
coffeeMachine.getCoffee();
}
}
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
public class CoffeeMachine {
private MachineStatus machineStatue;
private int cup;

public CoffeeMachine() {
this.machineStatue = new MachineStatusRun();
this.cup = 15;
}

public void getCoffee() {
machineStatue.click(this);
}

public int getCup() {
return cup;
}

public void useCup() {
--cup;
}

public boolean isEmpty() {
return getCup() == 0;
}

public boolean isClean() {
return getCup() % 10 == 0;
}

public void setMachineStatue(MachineStatus machineStatue) {
this.machineStatue = machineStatue;
}
}
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
public interface MachineStatus {
void click(CoffeeMachine coffeeMachine);
}

public class MachineStatusRun implements MachineStatus{
@Override
public void click(CoffeeMachine coffeeMachine) {
System.out.println("커피를 뽑습니다!");
coffeeMachine.useCup();
System.out.println("남은 커피 : " + coffeeMachine.getCup());

if(coffeeMachine.isEmpty())
coffeeMachine.setMachineStatue(new MachineStatusEmpty());
else if(coffeeMachine.isClean())
coffeeMachine.setMachineStatue(new MachineStatusClean());
}
}

public class MachineStatusClean implements MachineStatus {
@Override
public void click(CoffeeMachine coffeeMachine) {
System.out.println("청소중 입니다!");
coffeeMachine.setMachineStatue(new MachineStatusRun());
}
}

public class MachineStatusEmpty implements MachineStatus{
@Override
public void click(CoffeeMachine coffeeMachine) {
System.out.println("커피가 없습니다!");
}
}
Author: Song Hayoung
Link: https://songhayoung.github.io/2020/08/28/Design%20Pattern/StatePattern/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.