設計模式 - 狀態模式 (Behavioral Patterns - State Design Pattern)

前言

在介紹狀態模式前,必須先講解一下狀態模式與策略模式的關係。

先看兩張圖

策略模式

狀態模式

我們可以看到兩張類別圖可以說是一模一樣,但是差別在哪呢?

策略模式

  • 可以讓呼叫端轉換不同的演算法
  • 他算是主動的使用我們的function並且抽換

狀態模式

  • 給物件不同的狀態,並且限制他只能在這狀態做哪些事情
  • 他算是被動的被控制只能使用那些function

舉例

策略模式

以策略模式文章中的技能,技能Service可以控管要呼叫哪個Skill物件使用,去使用就適合策略模式

狀態模式

角色今天可能會有禁咒、石化,那我們需要先把角色可以做的事情抽出來(攻擊、移動、技能)創出一個行為介面,再依不同的狀態實做這個介面,而這些物件則可以在實作中根據狀態,限制這些行為。

範例

英雄今天可能會被施加石化、禁咒,那我們看一下狀態模式如何讓我們的程式碼,抽象話

  • 讓正常、石化、禁咒狀態實作State介面
  • 英雄呼叫State介面的方法(可抽換不同的狀態實作)
  • 再由Context(設定不同的狀態)
interface HeroState {
    void run();
    void useSkill();
    void attack();
}

class PetrochemicalState implements HeroState {

    @Override
    public void run() {
        System.out.println("Your are a stone so you can't do anything");
    }

    @Override
    public void useSkill() {
        System.out.println("Your are a stone so you can't do anything");
    }

    @Override
    public void attack() {
        System.out.println("Your are a stone so you can't do anything");
    }
}

class CursedState implements HeroState {

    @Override
    public void run() {
        System.out.println("You are running");
    }

    @Override
    public void useSkill() {
        System.out.println("Your are cursed so you can't use skill");
    }

    @Override
    public void attack() {
        System.out.println("Your are attacking");
    }
}

class NormalState implements HeroState {

    @Override
    public void run() {
        System.out.println("You are running");
    }

    @Override
    public void useSkill() {
        System.out.println("Your are using skill");
    }

    @Override
    public void attack() {
        System.out.println("Your are attacking");
    }
}

class Hero {

    HeroState heroState;

    public void attack() {
        heroState.attack();
    }

    public void run() {
        heroState.run();
    }

    public void useSkill() {
        heroState.useSkill();
    }

    public void setState(HeroState state) {
        this.heroState = state;
    }
}

class HeroActionService {
    public void doAllAction(Hero hero) {
        hero.attack();
        hero.run();
        hero.useSkill();
    }
}

public class State {
    public static void main(String[] args) {
        Hero hero = new Hero();
        HeroActionService heroActionService = new HeroActionService();
        System.out.println("Normal state");
        hero.setState(new NormalState());
        heroActionService.doAllAction(hero);
        hero.setState(new CursedState());
        heroActionService.doAllAction(hero);
        hero.setState(new PetrochemicalState());
        heroActionService.doAllAction(hero);
    }
}

原始碼

結論

  • 狀態模式適合用來表達,當下此物件不同狀態時可執行方法的差異

    • 石化甚麼都不可以做
    • 禁咒不可施法、但其他都可以
  • 策略模式,適合讓物件決定去做甚麼

    • 今天要用治療或是攻擊技能
  • 兩者最大差別在於意圖

    • 一個是我這個情況可以做哪些事情
    • 一個是接下來我要做甚麼(沒有情況)

留言

這個網誌中的熱門文章

Java Lambda Map篇

(InterviewBit) System Design - Design Cache System

設計模式 - 享元模式 (Structural Patterns - Flyweight Design Pattern)