Android 設計模式--策略模式

今天來看下策略模式

以下內容大部分來自《設計模式》一書,僅做了整理。

定義:策略模式定義了算法族,分別封裝起來,讓他們之間可以相互替換。


我們來設計一個場景:一個關於鴨子的遊戲,鴨子在水裏邊游泳,邊呱呱叫着。

超類:Duck.class

/**
 * 鴨子超類
 */
public abstract class Duck {

    /**
     * 鴨子會呱呱叫
     */
    public void qucak(){};

    /**
     * 鴨子會游泳
     */
    public void swim(){};

    /**
     * 鴨子的外觀,子類的鴨子都要自己去實現自己的外觀,所以display方法是抽象的
     */
    public abstract void display();

}
而鴨子的子類需要繼承Duck類,就可以實現呱呱叫,和游泳能力,只需要自己實現自己的外觀就可以了。

綠頭鴨:MallardDuck.class

/**
 * 綠頭鴨
 */
public class MallardDuck extends Duck{
    private static String TAG=MallardDuck.class.toString();
    @Override
    public void display() {
        Log.i(TAG,"----------這是綠頭鴨---------");
    }
}
紅頭鴨:RedHeadDuck.class

/**
 * 紅頭鴨
 */
public class RedHeadDuck extends Duck {
    private static String TAG = RedHeadDuck.class.toString();

    @Override
    public void display() {
        Log.i(TAG, "----------這是紅頭鴨---------");
    }
}
如果現在需求有改動說是這些鴨子要會飛,我們只需要在超類裏添加一個fly方法就可以了,而子類並不需要做改動

/**
 * 鴨子超類
 */
public abstract class Duck {

    /**
     * 鴨子會呱呱叫
     */
    public void qucak(){};

    /**
     * 鴨子會游泳
     */
    public void swim(){};

    /**
     * 鴨子的外觀,子類的鴨子都要自己去實現自己的外觀,所以display方法是抽象的
     */
    public abstract void display();
    /**
     * 飛行
     */
    public void fly(){};

}
然後產品經理跟你說我們需要向裏面添加一種鴨子,叫橡皮鴨。現在問題就出現了,橡皮鴨是玩具,並不會飛,也不會呱呱叫。

橡皮鴨:RubberDuck.class

/**
 * 橡皮鴨
 */
public class RubberDuck extends Duck{
    private static String TAG = RubberDuck.class.toString();

    /**
     * 橡皮鴨並不會呱呱叫,所以重寫父類呱呱叫方法
     */
    @Override
    public void qucak() {
        Log.i(TAG, "----------橡皮鴨不會呱呱叫---------");
    }

    @Override
    public void display() {
        Log.i(TAG, "----------這是橡皮鴨---------");
    }

    /**
     * 橡皮鴨也不會飛,重寫父類飛行方法
     */
    @Override
    public void fly() {
        Log.i(TAG, "----------橡皮鴨不會飛---------");
    }
}
這樣看似是一個可行的方案,可是鴨子的種類越來越多,鴨子會叫的、不會叫的、會游泳的、不會游泳的。。。。,也可能會增加一個鴨子屬性,而已有的鴨子對這個屬性又有不同的屬性,我們就必須再去每一個鴨子子類裏去重寫父類的這個方法,這樣一直在重寫父類的方法,導致代碼重複,工作量越來越大,這並不是我們想要看到的方案。

如果能有一種能夠建立軟件的方法,能夠讓我們對既有的代碼影響最小的方式來改動軟件,我們就可以花更少的時間重做代碼,節省大量的工作量、工作時間那該多好。

不管一個軟件設計的多好,一段時間後,需求總會做改動。

現在可以看出繼承(extends)並不能很好的解決這個問題,因爲你不知道在未來子類會新冒出一個什麼屬性。

所有我們將引出“針對接口編程”概念。

“針對接口編程”的真正意思是“針對超類型(supertype)編程”

這裏所謂的“接口”有多個含義,接口是一個“概念”,也是一種Java的interface構造。你可以在不涉及Java interface的情況下,“針對接口編程”,關鍵就在多態。利用多態,程序可以針對超類型編程,執行時會根據實際狀況執行到真正的行爲,不會被綁死在超類型的行爲上。“針對超類型編程”這句話,可以明確的說成“變量的聲明類型應該是超類,通常是一個抽象類或者是一個接口,如此,只要是具體實現此超類型的類所產生的對象,都可以指定給這個變量。這也意味着,聲明類時不用理會以後執行時的真正對象類型!”

---------------------------------------------------------------------下面纔是正文------------------------------------------------------------------------------------------

上面這些話是書裏說的,理解不了就別看了,簡單的說就是把易變的部分做成接口(interface)形式,在父類裏聲明一個接口變量,在子類裏給這個變量賦值。

下面上代碼:

鴨子的呱呱叫和飛行行爲是易變的,我們就把呱呱叫和飛行行爲做成接口形式。

呱呱叫接口:QuackInterface.class

/**
 * 呱呱叫行爲接口
 */
public interface QuackInterface {
    /**
     * 鴨子會呱呱叫
     */
    void qucak();
}
飛行行爲接口:FlyInterface.class

/**
 * 飛行行爲接口
 */
public interface FlyInterface {
    /**
     * 飛行
     */
    void fly();
}

以前的Duck.class重新寫

/**
 * 鴨子超類
 */
public abstract class Duck {
    /**
     * 爲行爲接口聲明兩個引用變量
     */
    QuackInterface quackInterface;//呱呱叫行爲接口
    FlyInterface flyInterface;//飛行行爲接口

    /**
     * 鴨子會游泳
     */
    public void swim(){};

    /**
     * 鴨子的外觀,子類的鴨子都要自己去實現自己的外觀,所以display方法是抽象的
     */
    public abstract void display();

    /**
     *  呱呱叫行爲委託給呱呱叫接口
     */
    public void perfromQuack(){
        quackInterface.qucak();
    }
    /**
     *  飛行行爲委託給飛行接口
     */
    public void perfromFly(){
        flyInterface.fly();
    }

}
綠頭鴨類重寫:MallardDuck.class

/**
 * 綠頭鴨
 */
public class MallardDuck extends Duck implements QuackInterface,FlyInterface{
    private static String TAG=MallardDuck.class.toString();
    @Override
    public void display() {
        Log.i(TAG,"----------這是綠頭鴨---------");
    }

    @Override
    public void qucak() {
        Log.i(TAG,"----------綠頭鴨呱呱叫---------");
    }
    @Override
    public void fly() {
        Log.i(TAG,"----------綠頭鴨會飛---------");
    }
}
橡皮鴨:RubberDuck.class

/**
 * 橡皮鴨
 */
public class RubberDuck extends Duck implements QuackInterface,FlyInterface{
    private static String TAG = RubberDuck.class.toString();

    @Override
    public void display() {
        Log.i(TAG, "----------這是橡皮鴨---------");
    }

    @Override
    public void qucak() {
        Log.i(TAG, "----------橡皮鴨不會呱呱叫---------");
    }

    @Override
    public void fly() {
        Log.i(TAG, "----------橡皮鴨不會飛---------");
    }

}
我們創建這些類之後,就可以使用了

Duck duck=new MallardDuck();
duck.perfromQuack();
duck.perfromFly();
Duck duck=new RubberDuck();
duck.perfromQuack();
duck.perfromFly();
這樣就可以創建出會飛、會呱呱叫的綠頭鴨和不會飛、不會叫的橡皮鴨了。

-------------------------------------------------------------動態設定行爲,下面的是更好的方案---------------------------------------------------------------------------------------------------

先拿一個飛行屬性舉例。

在Duck.class中加入一個方法:

public void setFlyInterface(FlyInterface flyInterface) {
    this.flyInterface = flyInterface;
}
完整Duck.class
/**
 * 鴨子超類
 */
public abstract class Duck {
    /**
     * 爲行爲接口聲明引用變量
     */
    FlyInterface flyInterface;//飛行行爲接口

    /**
     * 鴨子會游泳
     */
    public void swim(){};

    /**
     * 鴨子的外觀,子類的鴨子都要自己去實現自己的外觀,所以display方法是抽象的
     */
    public abstract void display();

    /**
     *  飛行行爲委託給飛行接口
     */
    public void perfromFly(){
        flyInterface.fly();
    }


    public void setFlyInterface(FlyInterface flyInterface) {
        this.flyInterface = flyInterface;
    }
}
新增兩個類

/**
 * 這個鴨子不會飛
 */
public class NoFlyClass implements FlyInterface{
    private static String TAG=NoFlyClass.class.toString();
    @Override
    public void fly() {
        Log.i(TAG, "----------我不會飛---------");
    }
}
/**
 * 這個鴨子會飛
 */
public class FlyClass implements FlyInterface{
    private static String TAG=FlyClass.class.toString();
    @Override
    public void fly() {
        Log.i(TAG, "----------我會飛---------");
    }
}
重寫綠頭鴨類:MallarDuck.class

/**
 * 綠頭鴨
 */
public class MallardDuck extends Duck{
    private static String TAG=MallardDuck.class.toString();

    @Override
    public void display() {
        Log.i(TAG,"----------這是綠頭鴨---------");
    }
}
重寫橡皮鴨類:RubberDuck

/**
 * 橡皮鴨
 */
public class RubberDuck extends Duck{
    private static String TAG = RubberDuck.class.toString();

    @Override
    public void display() {
        Log.i(TAG, "----------這是橡皮鴨---------");
    }

}
使用的時候

Duck mallardDuck=new MallardDuck();
mallardDuck.setFlyInterface(new FlyClass());
mallardDuck.perfromFly();

Duck rubberDuck=new RubberDuck();
rubberDuck.setFlyInterface(new NoFlyClass());
rubberDuck.perfromFly();
這篇文章到這就結束了。

如有錯誤或紕漏的地方,歡迎指正。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章