상태(State) 패턴이란?
• 객체 내부 상태에 맞춰 스스로 행동을 변경하는 패턴
• 객체는 마치 자신의 클래스를 바꾸는 것처럼 보인다.
• if-else와 같은 분기문으로 상태전이 하는 것을 해소한다.
기존 설계 가독성이 충분하다면 굳이 State 패턴을 적용할 필요 없다.
• 각 상태에 해당하는 클래스를 설계한다.
상태를 취급하는 만큼 클래스 개수가 증가한다.
• 디자인 패턴(Design Pattern) 중 행위패턴에 속한다.
if / switch 조건 분기
게임 캐릭터 공격(attack) 방법에는
맨손•칼•총 등 다양한 무기들이 있다.
가지고 있는 무기에 따라 상태가 달라진다.
쉽게 구현할 수 있는 방법은 분기문(if•switch)일 것이다.
#include <iostream>
using namespace std;
class Person
{
int item;
public:
void setItem(int item){
this->item = item;
}
void attack() {
if (item == 1)
cout << "주먹 휘두르기" << endl;
else if (item == 2)
cout << "검 휘두르기" << endl;
}
};
int main()
{
Person* p = new Person();
p->setItem(2);
p->attack();
}
이 방식은 새로운 무기가 추가될 때 if문을 추가해야 한다.
attack() 함수뿐만 아니라
다른 함수도 무기 변경 영향을 받는다면 OCP 원칙에 위배된다.
가상함수
변하는 부분을 가상함수로 처리해 볼 수 있다.
#include <iostream>
using namespace std;
class Person
{
int item;
public:
void attack() { doSth(); }
virtual void doSth() { std::cout << "주먹 휘두르기" << std::endl; }
};
class ItemSword : public Person
{
public:
void doSth() override { std::cout << "검 휘두르기" << std::endl; }
};
int main()
{
Person* p = new Person;
p->attack(); // 주먹 휟두르기
p = new ItemSword;
p->attack(); // 검 휘두르기
}
새로운 무기가 추가될 때,
무기(=State)에 맞는 클래스만 추가하면 된다.
기존 함수를 변경하지 않기에 OCP에 위배되지 않는다.
상태 패턴을 적용한 듯하지만
p = new ItemSword;은 상태 변경이라기 보다는
"검을 지닌 새로운 객체"를 만드는 형태라고 봐야될 것 같다.
인터페이스 설계
변해야 하는 멤버함수를 인터페이스로 설계
#include <iostream>
using namespace std;
struct IState
{
virtual void attack() = 0;
virtual void defence() = 0;
virtual ~IState() {}
};
class Person
{
int item;
IState* s = nullptr;
public:
void setState(IState* _s) { s = _s; }
void attack() {if(s) s->attack(); }
void defence() {if(s) s->defence(); }
};
class ItemNormal : public IState
{
public:
void attack() override { std::cout << "기본 공격" << std::endl; }
void defence() override { std::cout << "기본 방어" << std::endl; }
};
class ItemSword : public IState
{
public:
void attack() override { std::cout << "칼 휘두르기" << std::endl; }
void defence() override { std::cout << "칼로 방어" << std::endl; }
};
class ItemGun : public IState
{
public:
void attack() override { std::cout << "총 쏘기" << std::endl; }
void defence() override { std::cout << "총으로 방어" << std::endl; }
};
int main()
{
ItemNormal normal;
ItemSword sword;
ItemGun gun;
Person* p = new Person;
p->setState(&normal);
p->attack();
p->defence();
p->setState(&sword);
p->attack();
p->defence();
p->setState(&gun);
p->attack();
p->defence();
}
• 가상함수와 마찬가지로 새로운 Item이 만들어지면 클래스만 추가하면 된다.
→ 하나의 객체에 연관된 함수들을 묶을 수 있다.
• 인터페이스 설계 방식은 객체 내부에서 상태만 변경하는 것으로
기존 멤버 변수값을 유지할 수 있으며 실행시간에 변경할 수 있다.
→ 객체 행위가 내부 상태에 따라 달라할 수 있음
• 상태 변화가 되는 객체를 Context 객체라고 한다.
• 상태패턴에서 사용되는 클래스는 보통 멤버 데이터 없이
함수만 있는 경우가 많아 「싱글톤」으로 구현해도 좋다.
'까망 동네 > 디자인 패턴' 카테고리의 다른 글
💻 [디자인패턴] 전략 패턴 (Strategy Pattern) (1) | 2023.10.19 |
---|---|
[디자인패턴] 싱글턴 패턴 (0) | 2023.10.14 |
[프로그래밍] 디자인 패턴 필요성 (0) | 2022.08.29 |
MVC 패턴이란? (0) | 2022.08.28 |
팩토리 메서드 패턴 (Factory Method) (0) | 2022.07.19 |
댓글