策略模式(Strategy Pattern)

策略模式的原則:
1.將變動部分封裝起來。
2.針對介面寫程式,不是針對實踐寫程式。
3.多用合成,少用繼承。

假如我們開發一款角色扮演遊戲,遊戲每個角色都有攻擊、防禦的動作,我們可以定義一個包含以上行為的父類別Character,再透過繼承讓所有子類別,也就是我們實際的腳色,都可以使用到這些方法,以下為程式碼。

#include<iostream>
using namespace std;

class Character{
public:
    void attack(){cout << "進行攻擊" << endl;}
    void defend(){cout << "進行防禦" << endl;}
};
class Warrior : public Character{
};
class Magician : public Character{
};

int main() {
    Warrior myWarrior;
    Magician myMagician;
    myWarrior.attack();
    myMagician.defend();
    return 0;
}

經由繼承讓子類別可以去呼叫父類別擁有的方法,但是日子久了,假使遊戲需要更多種不同的玩法,必須增加攻擊方式和飛行行為,我們在父類別加入fly()方法,但這時候問題來了,我們讓龍騎士能夠飛行,但讓戰士飛行有點不合邏輯,因為戰士也繼承Character類別的飛行,要處理這種情況可以Override父類別的fly()方法,以下為程式碼。

#include<iostream>
using namespace std;

class Character{
public:
    virtual void attack(){cout << "進行攻擊" << endl;}
    void defend(){cout << "進行防禦" << endl;}
    virtual void fly(){cout << "進行飛行" << endl;}
};
class Warrior : public Character{
public:
    void attack(){cout << "進行普通攻擊" << endl;}
    void fly(){cout << "不進行飛行" << endl;}
};
class Magician : public Character{
public:
    void attack(){cout << "進行魔法攻擊" << endl;}
    void fly(){cout << "不進行飛行" << endl;}
};
class DragonRider : public Character{
public:
    void attack(){cout << "進行普通攻擊" << endl;}
};

int main() {
    Warrior myWarrior;
    Magician myMagician;
    DragonRider myDragonRider;
    myWarrior.attack();
    myMagician.attack();
    myDragonRider.fly();
    return 0;
}

這樣的做法其實不恰當,在目前的架構下加入新的行為時,每個角色都必須去實作此方法,當角色一多將會變得非常麻煩,而且如果攻擊或防禦每多一種變化就去增加一種實作方式,這樣會讓系統變的難以維護,這時可以改用策略模式。

原本戰士類別必須知道attack()方法的實際內容,修改後我們增加一個AttackBehavior類別,之後交給AttackBehavior類別去實際處理攻擊行為,把Warrior類別與攻擊方式的耦合降低,如此一來,戰士類別不用管attack()方法實際如何做,反正由AttackBehavior類別去處理,而且透過這個方式,我們可以在執行階段動態的改變攻擊行為,以下為程式碼。

#include<iostream>
using namespace std;

class AttackBehavior{
public:
    virtual void attack(){ cout << "進行攻擊" << endl; }
};
class DefendBehavior{
public:
    void defend(){ cout << "進行防禦" << endl; }
};
class FlyBehavior{
public:
    virtual void fly(){ cout << "進行飛行" << endl; }
};
class NormalAttack : public AttackBehavior{
public:
    void attack(){ cout << "進行普通攻擊" << endl; }
};
class MagicAttack : public AttackBehavior{
public:
    void attack(){ cout << "進行魔法攻擊" << endl; }
};
class FlyWithWing : public FlyBehavior{
public:
    void fly(){ cout << "進行飛行" << endl; }
};
class FlyNoWay : public FlyBehavior{
public:
    void fly(){ cout << "不進行飛行" << endl; }
};

class Character{
protected:
    AttackBehavior *m_attackBehavior;
    DefendBehavior *m_defendBehavior;
    FlyBehavior *m_flyBehavior;
public:
    Character(AttackBehavior *, DefendBehavior *, FlyBehavior *);
    void performAttack(){ m_attackBehavior->attack(); }
    void performDefend(){ m_defendBehavior->defend(); }
    void performFly(){ m_flyBehavior->fly(); }
    void setAttackMode(AttackBehavior *atk){ m_attackBehavior = atk; }
    void setFlyMode(FlyBehavior *fly){ m_flyBehavior = fly; }
};
Character::Character(AttackBehavior *attack, DefendBehavior *defend, FlyBehavior *fly){
    m_attackBehavior = attack;
    m_defendBehavior = defend;
    m_flyBehavior = fly;
}
class Warrior : public Character{
public:
    Warrior(AttackBehavior *attack, DefendBehavior *defend, FlyBehavior *fly) : Character(attack, defend, fly){}
};

class Magician : public Character{
public:
    Magician(AttackBehavior *attack, DefendBehavior *defend, FlyBehavior *fly) : Character(attack, defend, fly){}
};
class DragonRider : public Character{
public:
    DragonRider(AttackBehavior *attack, DefendBehavior *defend, FlyBehavior *fly) : Character(attack, defend, fly){}
};

int main() {
    AttackBehavior *attack = new NormalAttack();
    DefendBehavior *defend = new DefendBehavior();
    FlyBehavior *fly1 = new FlyNoWay();
    FlyBehavior *fly2 = new FlyWithWing();
    Warrior myWarrior(attack, defend, fly1);
    DragonRider myDragonRider(attack, defend, fly2);
    myWarrior.performAttack();
    myDragonRider.performFly();

    AttackBehavior *attack2 = new MagicAttack();
    myDragonRider.setAttackMode(attack2);
    myDragonRider.performAttack();

    delete attack;
    delete defend;
    delete fly1;
    delete fly2;
    delete attack2;
    system("PAUSE");
    return 0;
}