1. 程式人生 > >淺談開發中常用的設計模式

淺談開發中常用的設計模式

設計模式在開發中佔很重要的地位。在大型專案中使用好設計模式往往會取得事半功倍的效果。本篇部落格就介紹下幾種在開發中常用到的設計模式。

設計原則

先看下一些約定俗成的設計原則,其實要遵守以下所有原則很難,但開發過程中還是要有這樣的意識。

  • 找出應用中可能需要變化之處,把它們獨立出來,不要和那些不需要變化的程式碼混在一起。(封裝變化)
  • 針對介面程式設計,而不是針對實現程式設計。
  • 多用組合,少用繼承:用組合建立的系統具有很大的彈性,不僅可以將演算法封裝成類,也可以在執行時動態地改變行為。總結一句話就是,有一個比是一個更好。
  • 為互動物件之間的鬆耦合設計而努力。
  • 對擴充套件開放,對修改關閉。(裝飾者是很好的體現)
  • 要依賴抽象,不要依賴具體類。這就是依賴倒置原則。它更強調的是抽象,不能讓高層元件依賴低層元件。
  • 最少知識原則:只和你的密友談話。就是說當你在設計一個系統,不管任何物件,你都要注意它所互動的類有哪些。不要讓太多的類藕合在一起。
  • 單一模式:一個類應該只有一個引起變化的原因。

模式介紹

策略模式

策略模式定義是:定義演算法族,分別封裝起來,讓它們之間可以互相替換,演算法的變化獨立於使用演算法的客戶。
我們來模擬一種情形來理解這句話。有一群鴨子,有各種顏色的鴨,他們有的會飛,有的不會飛,飛的姿態也更不相同。此時如果在每隻鴨子裡分別定義飛的姿態,那有成千上萬中鴨子的時候就會累死。這時我們考慮使用設計模式來實現下它。
設計原則中第一條:找出應用中可能需要變化之處,把它們獨立出來。當然這裡我們獨立的就是會變化的飛行為。
設計模式第二條:針對介面程式設計,而不是針對實現程式設計。所以到這裡我們寫出如下飛的程式碼:

public interface FlyBehavior {
    public void fly();
}

定義了介面類,再定義實現類,會簡單,只定義會飛和不會飛行為:

public class NoFly implements FlyBehavior {

    @Override
    public void fly() {
        // TODO Auto-generated method stub
        System.out.println("this duck can not fly");
    }


}
public class CanFly
implements FlyBehavior{
@Override public void fly() { // TODO Auto-generated method stub System.out.println("this duck can fly"); } }

以上就是針對介面程式設計,那它有什麼好處呢,繼續往下看。
設計原則第三條:多用組合,少用繼承。那我們也來運用下。定義鴨子的抽象類:

public abstract class Duck {
    FlyBehavior flyBehavior;

    public abstract void disply();

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

    public FlyBehavior getFlyBehavior() {
        return flyBehavior;
    }
    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }

}

其中它有一個FlyBehavior物件,組合了飛的行為,典型的“有一個“而不是”有一個“。
好了,接下來就是實體鴨子, 只要繼承超類就可以了。

public class RedDuck extends Duck{

    public RedDuck() {
        // TODO Auto-generated constructor stub
        flyBehavior = new NoFly();//預設不會飛

    }
    @Override
    public void disply() {
        // TODO Auto-generated method stub
        System.out.println("the color is red");
    }

}

紅鴨不會飛,再來一隻黑鴨會飛:

public class BlackDuck extends Duck{

    public BlackDuck() {
        // TODO Auto-generated constructor stub
        flyBehavior = new CanFly();//預設不會飛

    }
    @Override
    public void disply() {
        // TODO Auto-generated method stub
        System.out.println("the color is black");
    }
}

最後我們來呼叫一下:

public class Main {
    public static void main(String[] args){
        Duck duck = new RedDuck();
        duck.performFly();
        duck.disply();
        duck.setFlyBehavior(new CanFly());
        duck.performFly();
        System.out.println("-----------------");
        Duck duck2 = new BlackDuck();
        duck2.performFly();
        duck2.disply();
        duck2.setFlyBehavior(new NoFly());
        duck2.performFly();
    }
}

這就是大名鼎鼎的策略模式。好處己經非常明顯了,要什麼飛行行為只要在執行時進行替換就可以了。這種模式的好處:實現不會被綁死在子類中,使用介面,可以執行時替換。看上它的UML圖:
這裡寫圖片描述

觀察者模式

定義了物件之間的一對多依賴,這樣一來,當一個物件改變時,它的所有依賴者都會收到通知並自動更新。
模擬場景的話很簡單,就是比如圖書館,你在圖書館借書,相當於你就是圖書館的一個觀察者,當圖書館有新聞釋出時你就會收到通知。
先看一下它的UML圖:
這裡寫圖片描述
從觀察人員入手:

public interface Observer {

    public void update(String name);
    public void disConnect();
}

只需要兩個方法,有訊息更新後會通知你(update)。如果你覺得煩也可以取消觀察。新建實現類小明:

public class XiaoMingObserver implements Observer{
    String bookName;
    private Subject subject;
    public XiaoMingObserver(Subject subject) {
        // TODO Auto-generated constructor stub
        this.subject = subject;
        subject.registerObserver(this);
    }
    @Override
    public void update(String name) {
        // TODO Auto-generated method stub

        System.out.println ("i am xiaoMing,the new book is "+name);
    }
    @Override
    public void disConnect() {
        // TODO Auto-generated method stub
        this.subject.removeObserver(this);
    }
}

小明擁有圖書證(有一個Subject 物件),subject註冊了它,它就成了一個觀察者,看下subject:

public interface Subject {
    public void registerObserver(Observer o);
    public void removeObserver(Observer o);
    public void notifyObservers();
}

實現三個功能,註冊觀察者,刪除觀察者,通知變化。可見它要擁有觀察者物件。看下具體的實現。

public class BookSubject implements Subject {
    private String bookName;
    ArrayList<Observer> aList; 
    public BookSubject() {
        // TODO Auto-generated constructor stub
        aList = new ArrayList<Observer>();
    }
    @Override
    public void registerObserver(Observer o) {
        // TODO Auto-generated method stub
            aList.add(o);

    }

    @Override
    public void removeObserver(Observer o) {
        // TODO Auto-generated method stub
        if(aList.contains(o)){
            aList.remove(o);
        }
    }

    @Override
    public void notifyObservers() {
        for(Observer observer :aList){
            if(observer instanceof XiaoGaoObserver){
                bookName = "java程式設計思想";
            }else{
                bookName = "c++程式設計思想";
            }

            observer.update(bookName);

    }
    }
}

registerObserver新增觀察者到List中。釋出通知也就是呼叫觀察者的Notify方法。觀察者在android原始碼中就很我地方體現,如Adapter中就是觀察資料變化就更改介面。以後有時間寫下Adapter的原始碼分析。

工廠模式

工廠模式有三種,個人感覺工廠模式還是比較複雜的。一步步講吧,最後會把原始碼附上。

1.簡單工廠模式

簡單工廠模式最好理解,平時用得也很多。簡單工廠模式主要是在初始化物件進行解耦合,工廠模式封裝了建立物件的細節。一般是靜態方法。但使用靜態方法有一個缺點,就是無法通過繼承來改變物件建立過程。
我們來模擬一個場景,假設我們在賣腸粉(最愛腸粉了。。。)。我們要做很多口味,這時不能做一種口味就new一個腸粉,因為做腸粉只有前面加料時步驟不一樣,後面的切跟蒸都是一樣的。先看上UML圖:
這裡寫圖片描述
所以我們用簡單工廠模式寫出以下程式碼:

public class SimpleChangfenStore {
    SimpleFactory mFactory = null;
    public SimpleChangfenStore(SimpleFactory factory){
        this.mFactory = factory;
    }
    public void orderChangfen(String tag){
        Changfen mChangfen = null;
        mChangfen = mFactory.createChangfen(tag);
        mChangfen.prepare();
        mChangfen.bake();
        mChangfen.cut();
        System.out.println("------finish one-------");
    }
}

createChangfen的作用就是用於抽取出來專門建立腸粉物件,這樣做的好處是可以防止你每次新增一種口味的腸粉都要修改這個類。看下它的程式碼:

public class SimpleFactory {
    public Changfen createChangfen(String tag){ 
        Changfen changfen = null;
        if(tag.equals("vegetable")){
            changfen = new VegetableChangfen();
    }else if(tag.equals("meat")){
        changfen = new MeatChangfen();
    }
    return changfen;
    }
}

根據tag建立不同的腸粉。最後來看下如何使用:

public class SimpleMain {
    public static void main(String[] args){
        SimpleFactory factory = new SimpleFactory();
        SimpleChangfenStore store = new SimpleChangfenStore(factory);
        store.orderChangfen("meat");
        store.orderChangfen("vegetable");
    }
}

很簡單,只要知道factory專門用來建立物件的就可以了。看下輸出結果:
這裡寫圖片描述
接下來看下工廠模式。

工廠模式

工廠模式:把建立changfen分為子類中,定義一個建立物件的介面,但由子類決定要例項化的類是哪一個,工廠方法讓類把例項化推遲到子類。
其實也很好理解,先看下UML圖:
這裡寫圖片描述
回到我們的腸粉店,現在我們的生意很好,要開分店啦,很開心。在深圳和汕頭更來一家店。那深圳和汕頭建立的店物件肯定是不同的嘛。所以類的例項化在到子類中,讓子類自己來建立。為此,我們的店要把建立物件的方法定義成抽象的。

public abstract class ChangfenStore {
    public abstract Changfen createChangfen(String tag);
    public void orderChangfen(String tag){
        Changfen mChangfen = null;
        mChangfen = createChangfen(tag);
        mChangfen.prepare();
        mChangfen.bake();
        mChangfen.cut();
        System.out.println("------finish one-------");
    }
}

createChangfen定義成抽象的了,要由子類來實現,看下深圳的子類:

public class SZStore extends ChangfenStore{

    @Override
    public Changfen createChangfen(String tag) {
        // TODO Auto-generated method stub
        Changfen changfen = null;
        if(tag.equals("vegetable")){
            changfen = new SZVegetableChangfen();
    }else if(tag.equals("meat")){
        changfen = new SZMeatChangfen();

    }
    return changfen;
    }

}

看吧,深圳用的是深圳的肉和菜,小看一眼:

public class SZVegetableChangfen extends Changfen{

    @Override
    public void prepare() {
        // TODO Auto-generated method stub
        System.out.println("i am in Shenzhen,i like the cabbage,so i add the cabbage");
    }

}

這樣就實現了工廠模式。看下如何使用:

public class Main {
    public static void main(String[] args){
        ChangfenStore SZstore = new SZStore();
        SZstore.orderChangfen("meat");
        SZstore.orderChangfen("vegetable");

        ChangfenStore STstore = new STStore();
        STstore.orderChangfen("meat");
        STstore.orderChangfen("vegetable");
    }
}

輸出如下:
這裡寫圖片描述
好啦 ,接下來講講抽象工廠方法啦。

抽象工廠方法

抽象工廠模式:提供一個介面,用於建立或依賴物件的家族,而不需要明確指定具體類。它提供了一個原料的生產工廠,原料的生產對客戶不可見。
不太好理解,其實就是建立一些依賴物件的原料。繼續看我們的腸粉。我們深圳店和汕頭店加的原料肯定是不同的,即使是用魚,那魚的種類也不一定相同。用一句話說就是深圳店和汕頭店要有自己的原料工廠。
原料嘛,就要有原料工廠,先看下原料工廠:

public abstract class AbstractChangfenFactory {
    public abstract Flour createFlour();
    public abstract Sauce createSauce();
    public abstract Seasoning createseasoning();

}

它是一個抽象類。腸粉要加醬加料等。具體加哪些醬哪些料,看具體實現類:

public class SZChangfenFactory extends AbstractChangfenFactory{
    @Override
    public Flour createFlour() {
        // TODO Auto-generated method stub
        return new SZFlour();
    }

    @Override
    public Sauce createSauce() {
        // TODO Auto-generated method stub
        return  new SZSauce();
    }

    @Override
    public Seasoning createseasoning() {
        // TODO Auto-generated method stub
        return new SZSessioning();
    }
}

看吧,是深圳特有的SZSauce,SZFlour等。這些都是物件,本例子只是簡單的列印一句話,這裡就不貼出來了,具體就不貼出來了。要記得汕頭也有自己的原料STSauce等。
原料都買好了,開始做腸粉吧:

public abstract class AbstractChangfen{
    protected Sauce mSauce = null;
    protected Flour mFlour = null;
    protected Seasoning mSeasioning = null;

    public abstract void prepare();
    public void bake(){
        System.out.println("after prepare,the Changfen should be bake");
    }
    public void cut(){
        System.out.println("after bake,the Changfen should be cut");
    }
}

看下深圳是怎麼做的,來到深圳分店視察:

public class STStore extends ChangfenStore{

    @Override
    public AbstractChangfen createChangfen(String tag) {
        AbstractChangfen changfen = null;
        AbstractChangfenFactory factory;
        factory = new STChangfenFactory();
        if(tag.equals("vegetable")){
            changfen = new STAbstractVegetableChangfen(factory);
    }else if(tag.equals("meat")){
        changfen = new STAbstractMeatChangfen(factory);

    }
    return changfen;
    }

}

深圳人嘛,要記得選擇深圳的原料工廠哦。選其中的做肉腸粉的程式碼來看一看:

public class STAbstractMeatChangfen extends AbstractChangfen{

    private AbstractChangfenFactory mFactory = null;
    public  STAbstractMeatChangfen(AbstractChangfenFactory factory) {
        // TODO Auto-generated constructor stub
        this.mFactory = factory;
    }
    @Override
    public void prepare() {
        // TODO Auto-generated method stub
        this.mSauce = this.mFactory.createSauce();
        this.mFlour = this.mFactory.createFlour();
        System.out.println("i am in ShanTou factory,i like meat,the sause is "+this.mSauce.print()+",the flour is "+this.mFlour.print());

    }

}

到此就一目瞭然了,深圳用深圳的原料,汕頭用汕頭的原料。輸出結果如下:
這裡寫圖片描述
到此工廠模式就講完了。看下UML圖。畫得比較複雜,細心看,能看懂的。
這裡寫圖片描述
篇幅關係,還有幾個常用設計模式下篇再總結。先到這吧。