3. VISITOR PATTERN 언제 쓰는가
• 의도: 클래스를 변경하지 않고 새로운 작업을 정의하는 방법
• 서로 다른 object 들이 모여 있는 배열, 트리, 또는 맵과 같은 집합이 있는데
• 각 object에 대해서 수행할 작업이 존재한다.
• 하지만 각 object 내에서 해당 작업에 대한 코드를 넣고 싶지 않을때
• 그렇다고 각 object의 type을 식별해서 casting 하고 싶지 않을때
• VISITOR PATTERN이 구현된 코드를 통해서 살펴보겠습니다
5. VISITOR PATTERN 구현 예제
• ELEMENT 추상 클래스
• 자연의 원소(element)들을 이용한 skill을 가지고 게임을 한다고 가정한다
• 원소는 Water, Fire, Air가 있다고 하자
• WATER, FIRE, AIR 클래스는 ELEMENT를 상속받음
• VISITOR 추상 클래스
• Water, Fire, Air에 대해서 visit() 함수를 구현할 수 있도록 virtual 함수 선언
• GET_SKILL_VISITOR 클래스 (VISITOR 상속)
• 각 Element를 획득했을 때 실행될 함수들을 구현
• LOSE_SKILL_VISITOR 클래스 (VISITOR 상속)
• 각 Element를 잃었을 때 실행될 함수들을 구현
6. ELEMENT 추상 클래스
// ELEMENT 추상 클래스
class Element
{
public:
virtual void Accept(Visitor& v) = 0; // 자식들이 Visitor를 받을 수 있도록
virtual void ToString() = 0; // 이건 그냥 화면에 보여주기 위함
};
7. ELEMENT 구현 클래스들
class Water : public Element
{
public:
void Accept(Visitor &v) { v.visit(this); }
void ToString() { printf_s("Watern"); }
};
class Fire : public Element
{
public:
void Accept(Visitor& v) { v.visit(this); }
void ToString() { printf_s("Firen"); }
};
class Air : public Element
{
public:
void Accept(Visitor& v) { v.visit(this); }
void ToString() { printf_s("Airn"); }
};
8. VISITOR 추상 클래스
class Visitor
{
public: // 입력 받을 각 클래스에 따라서 함수 선언
virtual void visit(class Water* element) = 0;
virtual void visit(class Fire* element) = 0;
virtual void visit(class Air* element) = 0;
};
10. MAIN 함수
Element *elementArr[] = {
new Water(), new Air(), new Fire() // 각 클래스 생성, 배열에 넣기
};
GetSkill_Visitor getSkill; // VISITOR 생성
LoseSkill_Visitor loseSkill; // VISITOR 생성
for (int i = 0; i < 3; i++)
elementArr[i]->Accept(getSkill); // GetSkill VISITOR를 배열의 각 원소에 실행
for (int i = 0; i < 3; i++)
elementArr[i]->Accept(loseSkill); // LoseSkill VISITOR를 배열의 각 원소에 실행
12. VISITOR PATTERN 장단점
• 단점:
• 새로운 클래스를 추가할 때마다, VISITOR 클래스 마다 METHOD를 추가해야 한다
• 클래스 설계 전에 VISITOR 패턴의 사용을 염두에 두고 만들어야 한다
• 이미 만들어진 코드에 적용하면 복잡해질 수 있다
• 장점:
• 클래스 마다 인터페이스가 다를 때, 각 클래스로부터 데이터 얻는 법을 캡슐화 할 수 있음
• 현재 코드가 없거나 코드를 변경할 수 없는 클래스에 대한 기능을 추가할 수 있다
• 서로 관련이 있는 작업들을 하나의 클래스로 묶을 수 있다
• 서로 관련이 없는 클래스들로부터 데이터를 모아서 처리할 수 있게 한다
14. STATE PATTERN 언제 쓰는가
• 의도: state가 변하면 object의 동작이 변하도록. 마치 다른 클래스처럼 동작하도록
• 구현방법:
• Wrapper 클래스를 만들어서 외부에는 단일 인터페이스를 노출시킨다
• State 추상 클래스 정의
• State 추상 클래스를 상속받아서 각 state를 정의 및 구현한다
• 현재 state에 대한 pointer를 context 클래스에서 유지한다
• State를 바꾸기 위해서는 state pointer를 바꾼다
16. STATE PATTERN 구현 예제
• Player 클래스는 현재 state (super, normal)에 따라서 공격력과 방어력이 달라짐
• Player가 state를 가리키는 wrapper 클래스이며,
• 각 state별로 attack와 defend 함수들이 다르게 작동하도록 만듬
17. PLAYER 클래스
class Player
{
public:
void SetCurrent(State* s)
{
m_Current = s;
}
void Attack() { m_Current->Attack(); }
void Defend() { m_Current->Defend(); }
private:
class State* m_Current;
};
18. STATE 추상 클래스
class State
{
public:
virtual void Attack() = 0;
virtual void Defend() = 0;
};
19. STATE의 구현 클래스들
class SuperMode : public State
{
public:
void Attack() {
printf_s(“2x damage!!n");
}
void Defend() {
printf_s("Armor 2x!n");
}
};
class NormalMode : public State
{
public:
void Attack() {
printf_s(“1x damage...n");
}
void Defend() {
printf_s("Armor none...n");
}
};
20. MAIN 함수
Player myPlayer;
SuperMode superMode;
myPlayer.SetCurrent(&superMode); // 슈퍼 모드르 state 세팅
myPlayer.Attack();
myPlayer.Defend();
NormalMode normalMode;
myPlayer.SetCurrent(&normalMode); // 보통 모드로 state 변경
myPlayer.Attack();
myPlayer.Defend();
23. FSM이 왜 필요?
• 액션 게임을 만들었다고 하자
• 플레이는 방향 키를 밑으로 누르면 앉을
수도 있고 (duck),
• B를 누르면 띌 수도 있다고 하자.
• 그리고 띄는 중에 방향 키를 밑으로 누
르면 다이빙한다고 하자
• 이를 그림으로 나타내면 좌측과 같다
24. FINITE STATE MACHINE 개념
• Fixed set of states
• Machine이 가질 수 있는 상태의 집합은 유한하다 (즉, 가능한 상태들이 정해져 있다)
• 앞의 액션 게임 예제에는, standing, ducking, jumping, diving이다
• 항상 하나의 state에 있어야 함
• 동시에 jumping과 diving을 할 수 없다. State 중복을 막는 것이 FSM 사용 이유
• Input 또는 event를 FSM 에게 보낸다
• Input에 따라서 state가 변할 수 있다 (transitions)
25. FINITE STATE MACHINE 장단점
• 장점
• 제한된 상태를 적용함으로써 복잡한 코드를 간단하게 만든다
• 단점
• 하나의 상태로 모든 것을 관리하기 어렵다
• Concurrent state machines 등 활용
26. FINITE STATE MACHINE 구현
• 앞의 State와 비슷한데, 차이점은
• State 사이의 전환은 State 클래스 자체적으로 한다
• FSM에게 Input또는 Event를 입력하면 FSM이 State전환을 한다