網頁

2015年11月5日 星期四

[Design Pattern] 策略模式 Strategy Pattern

想起當年剛開始學Java的時候,常覺得interface、abstract class是在幹嘛的、能吃嗎

感覺好像用不到的感覺,開始學了設計模式後才發現物件導向 "程式碼再利用" 的精神.




今天來介紹一個會用到 interface、abstract class 的設計模式,策略模式~

先不考慮程式,單純看名字(設計模式的名字很重要),策略 strategy:
什麼是策略? 對我來說策略是 針對同一個問題的不同解決方法
例如 填飽肚子 是你的問題,那 吃飯、吃麵、吃麵包 就個別是你的策略,都是能讓你填飽肚子的食物,就看你選擇哪一個。


[程式示範]

假設現在有一款遊戲,裡面有四個角色,騎士、法師、妖精、王子
(很熟悉?!代表我們同年代 )

以前我在寫都會把 "攻擊行為" 寫在個角色內.

騎士會揮劍
法師會放魔法
妖精會射弓劍
王子會...叫人

轉成Java
// 騎士
public class Knight {

    // 攻擊
    public void Attack(){
        System.out.println("揮劍");
    }
}

// 妖精
public class Elf {

    // 攻擊
    public void Attack(){
        System.out.println("射弓劍");
    }
}

// 王子
public class Prince {

    // 攻擊
    public void Attack(){
        System.out.println("叫人...");
    }
}

// 魔法師
public class Wizard {

    // 攻擊
    public void Attack(){
        System.out.println("放魔法");
    }
}

現在你想新增一個角色黑暗妖精,是不是又再多寫一個黑妖的類別,然後再寫他的攻擊方法...如果想增加其他技能,要個別增加,如果角色有100個...這太累人了

這時就是abstract class出場的時候,你可以增加一個角色的抽象類別

// 抽象類別 角色
public abstract class charater  {

    // 一般攻擊

    public abstract void attack();

    // 技能

    public abstract void ability();

    // 顯示角色

    public abstract void display();

}

再將各個職業角色繼承charater類別,基本上你所有的角色就有這三個方法了.

騎士範例
// 騎士
public class Knight extends Charater {

    // 一般攻擊
    @Override
    public void attack() {
        System.out.println("揮劍");
    }

    // 魔法攻擊
    @Override
    public void ability() {
        System.out.println("放魔法");
    }

    // 顯示角色
    @Override
    public void display() {
        System.out.println("我是騎士");
    }
}

現在想新增一個村子裡NPC的角色,NPC不會攻擊也不會放魔法,該怎麼辦呢?
直接空白不寫?那如果有個會攻擊的NPC怎麼辦?

這時可以利用interface,降臨!!!~

宣告兩個interface


  1. AttackHavior
  2. MagicAbility

public interface AttackBehavior {
    void attack();
}
public interface MagicAbility {
    void magic();
}

實作這兩個攻擊方式的類別
//揮劍攻擊
public class WieldAttack implements AttackBehavior {
    @Override
    public void attack() {
        System.out.println("揮劍");
    }
}
// 一級魔法
public class LevelOneMagic implements MagicAbility {
    @Override
    public void magic() {
        System.out.println("使用一級魔法");
    }
}
// 不會物理攻擊
public class AttackNoWay implements AttackBehavior {
    @Override
    public void attack() {
        System.out.println("不會攻擊");
    }
}
// 不會魔法攻擊
public class MagicNoWay implements MagicAbility {
    @Override
    public void magic() {
        System.out.println("不會放魔法");
    }
}

再將charater改成
// 抽象類別 角色
public abstract class Charater {

    // 物理攻擊行為
    AttackBehavior attackBehavior;
    // 魔法攻擊行為
    MagicAbility magicAbility;

    // 一般攻擊
    public void attack(){
        attackBehavior.attack();
    }

    // 技能
    public void ability(){
        magicAbility.magic();
    }

    // 顯示角色
    public abstract void display();
}

此時的騎士類別變成
// 騎士
public class Knight extends Charater {

    // 建構子預設攻擊行為
    public Knight() {
        this.attackBehavior = new WieldAttack();
        this.magicAbility = new LevelOneMagic();
    }

    // 顯示角色
    @Override
    public void display() {
        System.out.println("我是騎士");
    }
}

NPC類別
//NPC
public class NPC extends Charater {

    // 建構子預設攻擊行為
    public NPC() {
        this.attackBehavior = new AttackNoWay();
        this.magicAbility = new MagicNoWay();
    }
    
    // 顯示角色
    @Override
    public void display() {
        System.out.println("我是NPC");
    }
}

在主方法執行
Charater knight = new Knight();
knight.display();
knight.attack();
knight.ability();

Charater npc = new NPC();
npc.display();
npc.attack();
npc.ability();
便能看到
我是騎士
揮劍
使用一級魔法

我是NPC
不會攻擊
不會放魔法

那如果想動態設定物理攻擊和魔法攻擊呢?
只要在Charater類別上修改成
// 抽象類別 角色
public abstract class Charater {

    AttackBehavior attackBehavior;
    MagicAbility magicAbility;

    // 一般攻擊
    public void attack(){
        attackBehavior.attack();
    }

    // 技能
    public void ability(){
        magicAbility.magic();
    }

    // 顯示角色
    public abstract void display();
    
    // 設定物理攻擊
    public void setAttackBehavior(AttackBehavior attackBehavior){
        this.attackBehavior = attackBehavior;
    }
    
    // 設定魔法攻擊
    public void setMagicAbility(MagicAbility magicAbility){
        this.magicAbility = magicAbility;
    }
}


如此一來 你要增加其他新的攻擊跟魔法就更輕鬆了,物件導向真的很強大!!!
快去試試你的策略模式吧~~

沒有留言:

張貼留言