1. 程式人生 > >設計模式(1)—策略模式

設計模式(1)—策略模式

0.什麼是設計模式?
我們經常使用被人設計好的庫和框架,利用他們的API(Application Programming Interface)編寫我們的程式,但是我們得把它們“組合”起來。像小孩搭積木一樣,建成自己的大廈。其中你用到的“方式”,都是事先存在你腦海中的,這些“方式”都是抽象的,並不是一塊塊具體的積木。並且,你在搭建的過程中,希望搭建成的玩具簡潔,美觀,安全,可擴充套件性強…等等,都是基於你腦海中的“方式”而完成的。這些“方式”.是你在搭建過程中,所經歷的成功,失敗和借鑑別人的大廈得來的。這些“方式”就稱為設計模式。
設計模式比庫的等級更高,它將告訴我們如何組織類和物件解決問題。當然,在java優秀的類和庫中,也用到了設計模式,這就是原始碼優秀的原因。這些優秀的設計模式被記錄下來,從而知道我們的思想。
為什麼一再強調腦海中呢? 當然需要,首先你的腦海中要有基本的模式,才能在實現過程中寫出好的設計。
1.什麼是策略模式?
OO即(Object Oriented)面向物件。瞭解過高階語言的同學都知道,OOP(Object Oriented Programming)即面向物件程式設計,是重頭戲。它的特徵包括抽象,封裝,繼承,多型。
策略模式——定義演算法族,分別封裝起來,讓它們之間可以互相獨立替換,此模式讓演算法的變化獨立於使用演算法的客戶。

乍一看非常晦澀難懂,待我們舉一個小栗子,大家就會明白了

首先我們要了解幾個必要的原則:
(0)封裝變化:找出程式中會變化的方面,然後將其和固定不變的方面相分離。
(1)多用組合(combination),少用繼承
(2)針對介面程式設計,不針對實現程式設計。
這3個原則非常管用,將貫穿我們整個設計模式的學習過程當中。

下面開始最精彩的部分,你將體驗到設計模式的魅力。
先從簡單的一個實現開始,公司要求你做一個鴨子系統。鴨子我們都知道,一邊游泳,一邊呱呱叫。同時按特性還分為各種的鴨子,比如紅頭鴨子,綠頭鴨子。按材料分:真鴨子,橡皮鴨(不會飛,吱吱叫),木頭鴨子(不會飛,不會叫)
這並不難
現在我們要讓鴨子會飛 ,“理所當然”我們將在鴨子類中新增程式碼:
這裡寫圖片描述

繼承了鴨子類的橡皮鴨有個小功能“吱吱叫”(這不難,複寫父類即可),但是隨之而來的問題出現了:橡皮鴨也飛了起來!

同時,如果有木頭鴨子。繼承了鴨子類的木頭鴨也具備了 “飛”和“叫”的功能!這是完全錯誤的。

作為一個優秀的OO程式猿,這也不難:我們繼續複寫!

當有了第四種鴨子(不會飛,會叫),複寫!
第五種鴨子(會飛,不會叫),複寫!
第六種…第七種….
這樣寫至少會帶來幾個問題:
A.程式碼在多個子類中重複
B.改變父類,造成其他鴨子不要的改變(比如木鴨子不要飛)

所以我們帶來了一個原則來拯救你:分開變化和不會變化的部分:

我們在利用第二個原則:針對介面程式設計

如:我們用Flyable和Quackable介面

緊接著我們實現這兩個介面:這裡寫圖片描述
這裡寫圖片描述

現在,父類鴨子將飛行和叫動作“委託”(delegate)別人處理,而不是在父類中定義Duck的方法,在父類中,我們不關心叫介面的物件到底是什麼,我們只關心
該物件知道如何實現叫就夠了。
這裡寫圖片描述

好了,講到這裡,我們來寫一寫程式碼:

//鴨子父類
public abstract class Duck {
    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;
    public Duck(){

    }
    public abstract void display();

    public void performFly(){
        flyBehavior.fly();

    }
    public void performQuack(){
        quackBehavior.quack();

    }
    public void swim() {
        System.out.println("All duck float,even ddecoys!");
    }
}
//定義FlyBehavior 介面
public interface FlyBehavior{
    public void fly();

}
//實現FlyBehavior介面
public class FlyNoWay implements FlyBehavior {
    public void fly() {
        System.out.println("I cannot fly");
    }
}
public class FlyWithWings implements FlyBehavior {
    public void fly() {
        System.out.println("I am flying!");

    }
}
//定義QuackBehavior介面
public interface QuackBehavior {
    public void quack();
}
//實現QuackBehavior介面
public class Quack  implements QuackBehavior {
    public void quack() {
    System.out.println("Quack");
}
}

public class MuteQuack implements QuackBehavior{

         public void quack() {
            System.out.println("<<Silence>>");
        }
    }
public class Squeak implements QuackBehavior{

     public void quack() {
        System.out.println("Squak");
    }
}
//公司讓你建立的鴨子
public class MallardDuck  extends Duck{
         public MallardDuck () {
             quackBehavior = new Quack();
             flyBehavior = new FlyWithWings();

         }
         public void display(){
                 System.out.println("I am a real Mallard duck");

             }
}

//測試單元
public class MiniDuckSimulator{

    public static void main(String[] arg) {
        Duck mallard = new MallardDuck();
        mallard.performQuack();
        mallard.performFly();

    }
}

//輸出結果
Quack
I am flying!

另外,我們還可以動態設定行為,在鴨子執行時的行為也可以改變。這也是最開始的繼承方法中所沒有的。
在Duck類中,加入兩個新方法:

public void setFlyBehavior(FlyBehavior fb)
{   
    flyBehavior = fb;
}
public void setQuackBehavior(QuackBehavior qb)
{   
        qucakBehavior = qb;
}
// 新建一個鴨子
public class MallardDuck  extends Duck{
         public MallardDuck () {
             quackBehavior = new Quack();
             flyBehavior = new FlyWithWings();

         }
public void display(){
    System.out.println("I am a model duck");
     }
}
//實現FlyBehavior介面
public class FlyRocketPowered implements FlyBehavior{
public void fly(){
    System.out.println("i am flying with a rocket!");
}
} 
//測試單元
public class MiniDuckSimulator{

    public static void main(String[] arg) {
        Duck model= new ModelDuck();
        model.performFly();
        model.setFlyBehavior(new FlyRocketPowered());
        model.performFly();
    }
}

//執行結果
I cannot fly
i am flying with a rocket!