Search…

Design Pattern: State Pattern

26/08/20204 min read
Khái niệm, cách hiện thực và cách thức hoạt động của State Pattern.

Vấn đề đặt ra

Vấn đề trong việc xử lý các trạng thái của đối tượng khi lập trình các hoạt động. Khi cần xử lý đối tượng Object.

void Object::update(float delta)
{
    if (KeyPress(BUTTON_LEFT_ARROW))
    {
        this->velocity = MOVE_LEFT_VELOCITY;
        this->image = MOVE_LEFT_IMAGE;
    }
}

Vấn đề nảy sinh khi thêm trạng thái "Jump".

void Object::update(float delta)
{
    if (KeyPress(BUTTON_LEFT_ARROW))
    {
        this->velocity = MOVE_LEFT_VELOCITY;
        this->image = MOVE_LEFT_IMAGE;
    }

    if (KeyPress(BUTTON_SPACE))
    {
        this->velocity = JUMP_VELOCITY;
        this->image = JUMP_IMAGE;
    }
}

Khi nhấn Space thì Object nhảy liên tục và nhấn Space + Left Arrow để nhảy qua bên trái thì hình ảnh không chính xác.

Thêm thuộc tính m_Jumping kiểu bool để kiểm tra Object khi Jump và cập nhật lại như sau:

if (KeyPress(BUTTON_LEFT_ARROW))
{
	this->velocity = MOVE_LEFT_VELOCITY;
	if (!this->m_Jumping)
		this->image = MOVE_LEFT_IMAGE;
}

if (KeyPress(BUTTON_SPACE))
{
	if (!m_Jumping)
	{
		this->m_Jumping = true;
		this->velocity = JUMP_VELOCITY;
		this->image = JUMP_IMAGE;
	}
}

Vấn đề được giải quyết nhưng khi Object có thêm nhiều trạng thái khác thì phải thêm rất nhiều biến bool và điều kiện if … else …

Giải pháp

Mô hình State Pattern.

Mô hình State Pattern

Khái niệm cơ bản

State là “trạng thái” của đối tượng.

Ví dụ: 

  • Object có các state là Stand, Running, Jumping, ...
  • Scene có các state là Intro, Play, End, ...

State Pattern là cách tổ chức quản lý các State một cách hợp lý.

Đặc điểm

  • Giảm rắc rối trong quá trình xử lý các sự kiện.
  • Dễ dàng chỉnh sửa trong quá trình lập trình.

Hiện thực

Trong bài viết này, State Pattern được trình bày dưới 2 dạng: 

  • Enum kết hợp với Switch-Cases.
  • OOP.

Enum kết hợp với Switch – Cases

Xác định được các State của đối tượng và tạo Enum cho các State đó.

Ví dụ, Object có 3 loại State: Stand, MoveJump nên sẽ có Enum cho các State:

enum State
{
    STAND_STATE = 0,
    MOVE_STATE,
    JUMP_STATE
};

Áp dụng Enum State của đối tượng vào trong class đối tượng: 

Khai báo lớp Object

#ifndef __OBJECT_H__
#define __OBJECT_H__

#include <stdio.h>

class Object
{
public:
    void    update(float delta);
private:
    State    m_currentState;
};

#endif // !__OBJECT_H__

Định nghĩa lớp Object

#include "Object.h"

void    Object::update(float delta)
{
    switch (this->m_currentState)
    {
    case State::STAND_STATE:
        // Làm bất kỳ các hoạt động nào như mong muốn
        // khi đối tượng Object đang ở trong trạng thái Stand.
        break;
    case State::MOVE_STATE:
        // Tương tự Stand State.
        break;
    case State::JUMP_STATE:
        // Tương tự Stand State.
        break;
    default:
        break;
    }
}

OOP

Xác định các State của đối tượng, hiện thực State Pattern theo hướng OOP.

Tạo lớp State thuần ảo.

#ifndef __STATE_H__
#define __STATE_H__

#include <stdio.h>

class State
{
public:
    State();
    ~State();

    virtual    void enter() = 0;
    virtual    void update(float delta) = 0;
    virtual    void exit() = 0;
};

#endif // !__STATE_H__

Trong ví dụ này, cần override lại 3 phương thức:

  • Enter: được gọi khi chuyển từ State cũ sang State mới. Phương thức này khởi tạo các điều kiện cần thiết để phương thức Update hoạt động một cách hiệu quả.
  • Update: cập nhật của đối tượng khi đang ở trong một State bất kỳ.
  • Exit: được gọi khi kết thúc State cũ để chuyển sang State mới. Phương thức này kết thúc các điều kiện cần thiết đã khởi tạo từ phương thức Enter trước đó.

Tạo lớp StandState kế thừa từ State, overrride các phương thức cần thiết.

#ifndef __STAND_STATE_H__
#define __STAND_STATE_H__

#include "State.h"

class StandState : public State
{
public:
    StandState();
    ~StandState();

    void    enter();
    void    update(float delta);
    void    exit();
};

#endif // !__STAND_STATE_H__

Sau khi có đủ các State của đối tượng, áp dụng vào class của đối tượng:

Trong khai báo lớp Object, khai báo:

  • Thuộc tính State* m_currentState “chạy” State hiện tại của đối tượng
  • Phương thức void changeState(State* state) để thay đổi các State của đối tượng.
#ifndef __OBJECT_H__
#define __OBJECT_H__

#include <stdio.h>
#include "State.h"

class Object
{
public:
    Object();
    ~Object();

    void    update(float delta);
    
    void    changeState(State* state);
private:
    State*    m_currentState;
};

#endif // !__OBJECT_H__

Trong định nghĩa lớp Object, sử dụng thuộc tính và phương thức.

#include "Object.h"

Object::Object()
{
    m_currentState = new StandState();
}

Object::~Object()
{
    if (m_currentState != nullptr)
    {
        delete m_currentState;
        m_currentState = nullptr;
    }
}

void    Object::update(float delta)
{
    m_currentState->update(delta);
}

void    Object::changeState(State* state)
{
    if (m_currentState)
        m_currentState->exit();
    m_currentState = state;
    m_currentState->enter();
}

Tương tự đối với các State còn lại của đối tượng.

Phân biệt Game State và Object State:

  • Game State là các trạng thái của Game. Ví dụ các trạng thái của Game: IntroState – là State giới thiệu về game; PlayState – là State “chạy” game, ...
  • Object State là các trạng thái của Object. Ví dụ các trạng thái của Object: StandState – là State đứng yên của Object; MoveState – là State di chuyển của Object, ...

Khi áp dụng State Pattern, sử dụng Enum cho Object State và OOP cho GameState vì:

  • Object State của mỗi đối tượng là khác nhau, khi làm theo hướng OOP cần khởi tạo rất nhiều class khác nhau nên khó quản lý hết.
  • Game State thì các State của nó mang tính duy nhất nên có thể kết hợp OOP của State Pattern với Singleton Pattern để quản lý Game State đơn giản.
IO Stream

IO Stream Co., Ltd

30 Trinh Dinh Thao, Hoa Thanh ward, Tan Phu district, Ho Chi Minh city, Vietnam
+84 28 22 00 11 12
developer@iostream.co

383/1 Quang Trung, ward 10, Go Vap district, Ho Chi Minh city
Business license number: 0311563559 issued by the Department of Planning and Investment of Ho Chi Minh City on February 23, 2012

©IO Stream, 2013 - 2024