1. 程式人生 > >安卓常用6種設計模式總結

安卓常用6種設計模式總結

轉載自https://blog.csdn.net/u012583459/article/details/47079529

         和https://blog.csdn.net/u012583459/article/details/47079549

由於專案變更的頻繁性,作為一名程式設計師,我們需要掌握設計模式的必要性,就不言而喻~~,下面就是一些我自己學習的設計模式總結。 
接下來,主要是針對幾個比較常用模式進行講解,主要是以下幾種:

  • 觀察者模式
  • 介面卡模式
  • 代理模式
  • 工廠模式
  • 單例模式
  • 命令模式

1.觀察者模式(Observer Pattern)

 
釋義:觀察者模式定義了一種一對多的依賴關係,讓多個觀察者物件同時監聽某一個主題物件,這個主題物件在狀態上發生變化時,會通知所有觀察者物件,使他們能夠自動更新自己。 
故事理解:觀察者想知道公司所有MM的情況,只要加入公司的MM情報郵件組就行了,tom負責蒐集情報,當發現新情報時,不用一個一個通知我們,直接釋出給郵件組,我們作為訂閱者(觀察者)就可以及時收到情報啦。 
常見例項:1.BaseAdapter.registerDataSetObserver和BaseAdapter.unregisterDataSetObserver兩方法來向BaseAdater註冊、登出一個DataSetObserver ; 2.使用ContentObserver去監聽資料庫變化。 
適用場景
:1.當對一個物件的改變需要同時改變其他物件,而不知道具體有多少物件有待改變;2.當一個物件必須通知其它物件,而它又不能假定其它物件是誰.

觀察者模式主要有觀察者和被觀察者2個物件,在該模式中,Observable表示被觀察者,這個物件是一個抽象類,只能被繼承。 Observer表示觀察者,他是一個介面,所以觀察者可以有多個,實現了該介面的類都是屬於觀察者。 
這是網上一個生動細緻的demo: 
被觀察者:

public class MyPerson extends Observable {

    private int age;
    private String name;
    private String sax;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
        setChanged();
        notifyObservers();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        setChanged();
        notifyObservers();
    }

    public String getSax() {
        return sax;
    }

    public void setSax(String sax) {
        this.sax = sax;
    }

    @Override
    public String toString() {
        return "MyPerson [age=" + age + ", name=" + name + ", sax=" + sax + "]";
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

MyPerson是被觀察者,類中呼叫了setChange()以及notifyObservers()兩個方法,前者是告知資料改變,後者是傳送訊號通知觀察者。

觀察者:

public class MyObserver implements Observer {

    private int i;
    private MyPerson myPerson;//觀察的物件

    public MyObserver(int i){
        System.out.println("我是觀察者---->" + i);
        this.i = i;
    }

    public int getI() {
        return i;
    }

    public void setI(int i) {
        this.i = i;
    }

    public MyPerson getMyPerson() {
        return myPerson;
    }

    public void setMyPerson(MyPerson myPerson) {
        this.myPerson = myPerson;
    }

    @Override
    public void update(Observable observable, Object data) {
        System.out.println("觀察者---->"+ i +"得到更新!");
        this.myPerson = (MyPerson)observable;
        System.out.println(((MyPerson)observable).toString());
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

觀察者需要實現Observer介面,其中只有一個update方法,當觀察者收到 (被觀察者)的通知訊號,就會執行該動作。

具體demo原始碼以及實現效果可點連線

2.介面卡模式(Adapter Pattern) 
釋義:把一個類的介面變換成客戶端所期待的另一種介面,從而使原本因介面原因不匹配而無法一起工作的兩個類能夠一起工作。適配類可以根據引數返還一個合適的例項給客戶端。 
故事理解:在朋友聚會上碰到了一個美女Sarah,從香港來的,可我不會說粵語,她不會說普通話,只好求助於我的朋友kent了,他作為我和Sarah之間的Adapter,讓我和Sarah可以相互交談了(也不知道他會不會耍我)。 
常見例項:ListView用於顯示列表資料,但是作為列表資料集合有很多形式,有Array,有Cursor,我們需要對應的介面卡作為橋樑,處理相應的資料(並能形成ListView所需要的檢視)。 
適用場景:1.業務的介面與工作的類不相容,(比如:類中缺少實現介面的某些方法)但又需要兩者一起工作;2. 在現有介面和類的基礎上為新的業務需求提供介面。

介面卡模式分為類介面卡模式和物件介面卡模式。關於類適配模式,因為java的單繼承,所以在已繼承一個類時,另外的只能是介面,需要手動實現相應的方法,這樣在客戶端就可以建立任一種符合需求的子類,來實現具體功能。而另外一種物件介面卡,它不是使用繼承再實現的方式,而是使用直接關聯,或者稱為委託的方式,具體可見該部落格詳細介紹介面卡模式(Adapter):類介面卡、物件介面卡

接下來就以ListView與ArrayAdapter來講解下 
ListAdapter:

public interface ListAdapter {
    public int getCount();
    Object getItem(int position);
    long getItemId(int position);
    View getView(int position, View convertView, ViewGroup parent);
    boolean isEmpty();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

ListView作為一個客戶端,所需要的目標介面就是ListAdapter,包含getCount(),getItem(),getView()等幾個方法,為了相容List< T >資料型別的資料來源,專門定義了ArrayAdapter介面卡,說白了,就是針對目標介面對資料來源進行相容修飾。

抽象類BaseAdapter,省略了其他程式碼,這裡只列出兩個方法:

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
    // ... ...
    public View getDropDownView(int position, View convertView, ViewGroup parent) {
        return getView(position, convertView, parent);
    }
    public boolean isEmpty() {
        return getCount() == 0;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

ArrayAdapter對List< T >進行封裝成ListAdapter的實現,滿足ListView的呼叫:

public class ArrayAdapter<T> extends BaseAdapter implements Filterable {
    private List<T> mObjects;
    //我只列出這一個建構函式,大家懂這個意思就行
    public ArrayAdapter(Context context, int textViewResourceId, T[] objects) {
        init(context, textViewResourceId, 0, Arrays.asList(objects));
    }

    private void init(Context context, int resource, int textViewResourceId, List<T> objects) {
        mContext = context;
        mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        mResource = mDropDownResource = resource;
        mObjects = objects; //引用物件,也是表達了組合優於繼承的意思
        mFieldId = textViewResourceId;
    }
    public int getCount() {
        return mObjects.size();
    }

   public T getItem(int position) {
        return mObjects.get(position);
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        return createViewFromResource(position, convertView, parent, mResource);
    }
    // ... ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

我們就如此成功的把List< T > 作為資料來源以ListView想要的目標介面的樣子傳給了ListView。這其實也是物件介面卡的一種。

3.代理模式(Proxy Pattern) 
釋義: 通過引入一個新的物件來實現對真實物件的操作或將新的物件作為真實物件的一個替身,這樣的實現機制是代理模式(為其他物件提供一種代理以控制對這個物件的訪問). 
故事理解:校園代理是為他對應的上司作代理,而這個校園代理的工作就是訪問校園中的學生,例如對學生進行問卷之類的事。學生就是官方說法中的其他物件,校園代理的上司就通過控制這個校園代理來控制對學生的訪問。 
常見例項:ActivityManagerProxy類就是一個代理,它是ActivityManagerNative的代理,也就是說ActivityManagerProxy是所說的Proxy類,而ActivityManagerNative就相當於“上司角色“類,它們都有一個共有的介面IActivityManager。ActivityManager,它相當於代理模式的client。在這個類中,可以看到大量的getxxx函式,這些函式,都會呼叫到ActivityManagerNative類的getDefault()方法,而該方法會獲得一個共用的單例的IActivityManager引用,然後通過多型來呼叫代理中的實現 
適用場景:代理模式的應用場合分幾種:遠端代理,虛擬代理,安全代理等,具體可見為別人做嫁衣—-代理模式

請看下面一張結構圖(網上看到的一個細緻生動的例子):

這裡寫圖片描述

Subject類:定義了RealSubject和Proxy的共用介面,這樣就可以在任何 使用RealSubject的地方都可以用Proxy。 
RealSubject類:定義Proxy所代表的真實實體。 
Proxy類:儲存一個引用使得代理可以訪問實體,並提供一個與Subject的介面相同的介面,這樣代理就可以用來替代實體。

接下來,我們來實現該模式: 
1.Subject類 :Image.java

/**
 * Subject類
 */
public abstract class Image {

    public abstract void displayImage();

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2.RealSubject類:RealImage.java


import com.andyidea.patterns.subject.Image;  

/**  
 * RealSubject類  
 */  
public class RealImage extends Image {  
    private String filename;  
    public RealImage(String filename) {   
        this.filename = filename;  
        loadImageFromDisk();  
    }  

    private void loadImageFromDisk() {  

        System.out.println("Loading   " + filename);  
    }  

    @Override  
    public void displayImage() {  

        System.out.println("Displaying " + filename);   
    }  

} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

3.proxy類:ProxyImage.java

import com.andyidea.patterns.realsubject.RealImage;
import com.andyidea.patterns.subject.Image;

/**
 * Proxy類
 */
public class ProxyImage extends Image {
    private String filename;
    private Image image;

    public ProxyImage(String filename) { 
        this.filename = filename; 
    }

    @Override
    public void displayImage() {

        if(image == null){
            image = new RealImage(filename);
        }
        image.displayImage();
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

4.客戶端測試類:ProxyClient.java

import com.andyidea.patterns.proxy.ProxyImage;
import com.andyidea.patterns.subject.Image;

/**
 * 代理模式客戶端測試類
 */
public class ProxyClient {

    public static void main(String[] args) {
        System.out.println("Welcome to my Blog!" +"\n" 
                   +"Proxy Patterns." +"\n"
                   +"-------------------------------");

        Image mImage1 = new ProxyImage("My.Image1");
        Image mImage2 = new ProxyImage("My.Image2");

        mImage1.displayImage();
        mImage2.displayImage();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

執行結果如下:

        Welcome to my Blog!  
        Proxy Patterns
        ------------------------------- 
        Loading   My.Image1 
        Displaying My.Image1 
        Loading   My.Image2  
        Displaying My.Image2
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

總結來說,代理類就是對實體儲存一個引用,同時也實現了實體一樣的介面方法,這樣,就可以替代實體了!!

1.工廠模式(Factory Pattern)

工廠模式分為簡單工廠模式,工廠方法模式以及抽象工廠模式

  • 簡單工廠模式:一般情況下,提供一個方法,方法的引數是一個標誌位,根據標誌位來建立不同的物件,這樣呼叫的時候只需要提供一個標誌位就可以建立一個實現了介面的類。
  • 工廠方法模式:將簡單工廠模式的那個方法分開,不再是在工廠方法中根據標誌位建立物件了。而是定義一個工廠介面,然後想建立幾個不同型別的物件(即實現了同一介面的不同java類),就建立了幾個不同型別的工廠。也就是建立的物件和建立物件的工廠是一一對應的。然後客戶端呼叫的時候直接去例項化一個具體的物件工廠,建立相對應的物件。
  • 抽象工廠模式:其實這個名起的有點不知所云沒有表達出這個模式的特點。其實這個模式就是工廠方法模式的稍微擴充套件一下而已。工廠方法模式裡面,一般一個工廠介面只有一個方法,比如createMouse()。然後用實現了這個介面的具體工廠類只能生產滑鼠。而抽象工廠模式就是一個工廠介面有多個方法,比如createMouse() , createKeyboard() 。 這樣實現了這個工廠介面的具體工廠類就可以既生產滑鼠又生產鍵盤。

常見例項:比如android的bitmap中常用的BitmapFactory類,建立Bitmap物件,通常使用靜態工廠方法

這裡主要介紹簡單工廠與工廠方法的區別: 
就以大話模式中小菜跟大鳥舉得雷鋒故事作為題材吧。 
LeiFeng類:

//雷鋒  
public interface LeiFeng {  
    void sweep();//掃地  
    void wash();//洗衣  
    void buyrice();//做飯  
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Student類:

//學做雷鋒的大學生  
public class Student implements LeiFeng{  

    public void buyrice() {  
        System.out.println("大學生做飯");  
    }  

    public void sweep() {  
        // TODO Auto-generated method stub  
        System.out.println("大學生掃地");  
    }  

    public void wash() {  
        // TODO Auto-generated method stub  
        System.out.println("大學生洗衣");  
    }  

} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

Valuator類志願者:

//學做雷鋒的志願者  
public class Valuator implements LeiFeng{  

    public void buyrice() {  
        System.out.println("志願者做飯");  
    }  

    public void sweep() {  
        // TODO Auto-generated method stub  
        System.out.println("志願者掃地");  
    }  

    public void wash() {  
        // TODO Auto-generated method stub  
        System.out.println("志願者洗衣");  
    }  

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

然後簡單工廠是這麼實現的:

//使用簡單工廠  
public class SimpleFactory {  

    public static LeiFeng createLeiFeng(String type){  
        if("大學生".equals(type)){  
            return new Student();  
        }else if("志願者".equals(type)){  
            return new Valuator();  
        }  
        return null;  
    }  
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

而工廠方法模式中,則多了一個介面去建立不同型別的物件: 
Factory類:

//工廠方法模式,工廠介面  
public interface Factory {  
    LeiFeng createLeiFengFactory();  
}
  • 1
  • 2
  • 3
  • 4

StudentFactory學生工廠:

//學生工廠  
public class StudentFactory implements Factory{  
    public LeiFeng createLeiFengFactory() {  
        return new Student();  
    }  
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

ValuatorFactory志願者工廠:

//志願者工廠  
public class ValuatorFactory implements Factory{  
    public LeiFeng createLeiFengFactory() {  
        return new Valuator();  
    }  
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

當我們實現起來時:

public static void main(String[] args) {  

    //簡單工廠模式
    LeiFeng f11=SimpleFactory.createLeiFeng("大學生");  
    f11.buyrice();  
    LeiFeng f22=SimpleFactory.createLeiFeng("大學生");  
    f22.wash();  

    //使用工廠方法模式  
    Factory fac=new StudentFactory();
    LeiFeng f4=fac.createLeiFengFactory();  
    f4.buyrice();  
    LeiFeng f5=fac.createLeiFengFactory();  
    f5.wash();    
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

這裡就要說說為什麼要使用工廠方法模式,因為簡單工廠使用起來明顯要方便簡約的多。從理論的角度來說,工廠方法模式更符合封閉-開放原則。即對修改封閉對擴充套件開放。試想後期維護過程中要增加一個種類的物件,也就是增加對介面的一種實現,簡單工廠模式就要在switch…case中增加一個case項,無疑是修改了工廠方法。如果是jar包模式的,就要重新發包了。但是工廠方法模式,完全不需要更改工廠介面,只是新增加一個實現的工廠類即可(如果是jar包模式的,就可以不用重新發jar包,讓用jar包的人自己去擴充套件一個實現了工廠介面的具體工廠類即可)。完全符合封閉-擴充套件原則。

2.單例模式(Single Pattern) 
釋義:單例模式確保某一個類只有一個例項,而且自行例項化並向整個系統提供這個例項單例模式。單例模式只應在有真正的“單一例項”的需求時才可使用。 
故事理解:俺有6個漂亮的老婆,她們的老公都是我,我就是我們家裡的老公Sigleton,她們只要說道“老公”,都是指的同一個人,那就是我 
常見例項:資料庫建立時使用單例模式,Servlet環境下共享同一個資源或者物件 
適用場景:對於定義的一個類,在整個應用程式執行期間只有唯一的一個例項物件。如Android中常見的Application物件。

單例模式可分為餓漢式,懶漢式等: 
(一)餓漢式:其特點是應用中尚未需要用到此單一例項的時候即先例項化。

public class SingleTon {

    // 靜態例項變數,直接初始化
    private static SingleTon instance = new SingleTon();

    // 私有化建構函式
    private SingleTon() {

    }

    // 靜態public方法,向整個應用提供單例獲取方式
    public static SingleTon getInstance() {
        return instance;
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

(二)懶漢式:其特點是延遲載入,即當需要用到此單一例項的時候,才去初始化此單一例項。

public class SingletonA {    

    /**  
     * 單例物件例項  
     */    
    private static SingletonA instance = null;    

    public static SingletonA getInstance() {    
      if (instance == null) {     //line 12                            
          instance = new SingletonA();          //line 13    
       }    
        return instance;    
    }    
}   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在這裡要說下懶漢式,因為它具有一定的缺陷,我們可以假設這樣的場景:兩個執行緒併發呼叫Singleton.getInstance(),假設執行緒一先判斷完instance是否為null,既程式碼中的line 12進入到line 13的位置。剛剛判斷完畢後,JVM將CPU資源切換給執行緒二,由於執行緒一還沒執行line 13,所以instance仍然是空的,因此執行緒二執行了new Signleton()操作。片刻之後,執行緒一被重新喚醒,它執行的仍然是new Signleton()操作。

所以對它進行了改良:

public class SingletonB {    

    /**  
     * 單例物件例項  
     */    
    private static SingletonB instance = null;    

    public synchronized static SingletonB getInstance() {    
      if (instance == null) {     //line 12                            
          instance = new SingletonB();          //line 13    
       }    
        return instance;    
    }    
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

往方法上加了個同步鎖,這樣就可以保證不會出執行緒問題了,但是這裡有個很大(至少耗時比例上很大)的效能問題。除了第一次呼叫時是執行了SingletonB的建構函式之外,以後的每一次呼叫都是直接返回instance物件。返回物件這個操作耗時是很小的,絕大部分的耗時都用在synchronized修飾符的同步準備上,因此從效能上說很不划算。

所以又進行了改進:

public class SingletonC {  

    /** 
     * 單例物件例項 
     */  
    private static SingletonKerriganD instance = null;  

    public static SingletonC getInstance() {  
        if (instance == null) {  
            synchronized (SingletonC.class) {  
                if (instance == null) {  
                    instance = new SingletonC();  
                }  
            }  
        }  
        return instance;  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

目前我用的版本也就是這種。然而,網上有人又對這種單例模式進行了改進,因為還是存在缺陷,具體可以去網上拓展下。再說說餓漢式的寫法,這種寫法不會出現併發問題,在ClassLoader載入類後例項就會第一時間被建立。但餓漢式的建立方式在一些場景中將無法使用:譬如例項的建立是依賴引數或者配置檔案的,在getInstance()之前必須呼叫某個方法設定引數給它,那樣這種單例寫法就無法使用了。

3.命令模式(Command Pattern) 
釋義:把一個請求或者操作封裝到一個物件中。命令模式把發出命令的責任和執行命令的責任分割開,委派給不同的物件。命令模式允許請求的一方和傳送的一方獨立開來,使得請求的一方不必知道接收請求的一方的介面,更不必知道請求是怎麼被接收,以及操作是否執行,何時被執行以及是怎麼被執行的。 
故事理解:俺有一個MM家裡管得特別嚴,沒法見面,只好藉助於她弟弟在我們倆之間傳送資訊,她對我有什麼指示,就寫一張紙條讓她弟弟帶給我。這不,她弟弟又傳送過來一個COMMAND,為了感謝他,我請他吃了碗雜醬麵,哪知道他說:“我同時給我姐姐三個男朋友送COMMAND,就數你最小氣,才請我吃麵。 
常見例項:常用的Runnable(在java.lang包下),其實就是用了命令模式,具體的體現過程,可見該部落格 
Runnable下的命令設計模式 
適用場景:1. 命令的傳送者和命令執行者有不同的生命週期。命令傳送了並不是立即執行。2. 命令需要進行各種管理邏輯。3. 需要支援撤消\重做操作(這種狀況的程式碼大家可以上網搜尋下,有很多,這裡不進行詳細解讀)。

其實經典的命令模式包括4個角色: 
Command:定義命令的統一介面 
ConcreteCommand:Command介面的實現者,用來執行具體的命令,某些情況下可以直接用來充當Receiver。 
Receiver:命令的實際執行者 
Invoker:命令的請求者,是命令模式中最重要的角色。這個角色用來對各個命令進行控制。

這裡寫圖片描述

接下來,就以小菜大鳥去燒烤店,給服務員報菜,然後服務員通知廚師為例子。 
Command類 :

/* 
 * 抽象命令 
 */  
abstract class Command {  
    protected Barbecuer barbecuer;  

    public Command(Barbecuer barbecuer) {  
        this.barbecuer = barbecuer;  
    }  

    // 執行命令  
    public abstract void excuteCommand();  
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

(Receiver類)Barbecuer:

/* 
 * 烤肉串者 
 */  
class Barbecuer {  

    // 烤羊肉串  
    public void bakeMutton() {  
        System.out.println("烤羊肉串!");  
    }  

    // 烤雞翅  
    public void bakeChickenWing() {  
        System.out.println("烤雞翅!");  
    }  
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

(ConcreteCommand類) BakeMuttonCommand、BakeChickenWingCommand:

/* 
 * 烤羊肉串命令 
 */  
class BakeMuttonCommand extends Command {  

    public BakeMuttonCommand(Barbecuer barbecuer) {  
        super(barbecuer);  
    }  

    @Override  
    public void excuteCommand() {  
        barbecuer.bakeMutton();  
    }  

    @Override  
    public String toString() {  
        return "命令模式,烤羊肉串命令!";  
    }  

} 

/* 
 * 烤雞翅命令 
 */  
class BakeChickenWingCommand extends Command {  

    public BakeChickenWingCommand(Barbecuer barbecuer) {  
        super(barbecuer);  
    }  

    @Override  
    public void excuteCommand() {  
        barbecuer.bakeChickenWing();  
    }  

    @Override  
    public String toString() {  
        return "命令模式,烤雞翅命令!     ";  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

(Invoker類)Waiter :

/* 
 * 服務員 
 */  
class Waiter {  
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
    private List<Command> orders = new ArrayList<Command>();  

    // 設定訂單  
    public void setOrder(Command command) {  
        orders.add(command);  
        System.out.println("增加訂單:" + command.toString() + " \t時間:" + simpleDateFormat.format(new Date()));  
    }  

    // 取消訂單  
    public void cancelOrder(Command command) {  
        orders.remove(command);  
        System.out.println("取消訂單:" + command.toString() + " \t時間:" + simpleDateFormat.format(new Date()));  
    }  

    // 通知全部執行  
    public void notifyA() {  
        for (Command command : orders) {  
            command.excuteCommand();  
        }  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

Client類實現(如下):

public class CommandMode {  
    public static void main(String[] args) {  
        // 開店前準備  
        Barbecuer boy = new Barbecuer();  
        Command bakeMuttonCommand1 = new BakeMuttonCommand(boy);  
        Command bakeMuttonCommand2 = new BakeMuttonCommand(boy);  
        Command bakeChickenWingCommand = new BakeChickenWingCommand(boy);  
        Waiter girl = new Waiter();  

        // 開門營業  
        girl.setOrder(bakeMuttonCommand1);  
        girl.setOrder(bakeMuttonCommand2);  
        girl.setOrder(bakeChickenWingCommand);  

        // 點菜完畢,通知廚房  
        girl.notifyA();  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

總結來說,命令模式是將功能提升到物件來操作,以便對多個功能進行一系列的處理以及封裝。 
這裡要建議下命令模式的使用,當我們不清楚是否需要使用時,一般不用著急去實現它。事實上,在需要的時候通過重構實現這個模式並不困難,只有在真正需要如撤銷/恢復操作等功能時,把原來的程式碼重構為命令模式才有意義。

最後,設計模式的運用,有助於程式碼的維護與拓展。任何模式的出現,都是為了解決一些特定的場景的耦合問題,以達到對修改封閉,對擴充套件開放的效果。

六種模式,學習它們,提高自己!除了這六種,還有幾種比較常用的,接下來會繼續思考,繼續熟悉!