1. 程式人生 > >設計原則與設計模式

設計原則與設計模式

什麼是設計原則?

1. 單一職責原則(SRP) 

定義:就一個類而言,應該僅有一個引起它變化的原因。 
從這句定義我們很難理解它的含義,通俗講就是我們不要讓一個類承擔過多的職責。如果一個類承擔的職責過多,就等於把這些職責耦合在一起,一個職責的變化可能會削弱或者抑制這個類完成其他職責的能力。這種耦合會導致脆弱的設計,當變化發生時,設計會遭受到破壞。 
比如我經常看到一些Android開發在Activity中寫Bean檔案,網路資料處理,如果有列表的話Adapter 也寫在Activity中,問他們為什麼除了好找也沒啥理由了,把他們拆分到其他類豈不是更好找,如果Activity過於臃腫行數過多,顯然不是好事,如果我們要修改Bean檔案,網路處理和Adapter都需要上這個Activity來修改,就會導致引起這個Activity變化的原因太多,我們在版本維護時也會比較頭疼。也就嚴重違背了定義“就一個類而言,應該僅有一個引起它變化的原因”。 
當然如果想爭論的話,這個模式是可以引起很多爭論的,但請記住一點,你寫程式碼不只是為了你也是為了其他人。
2. 開放封閉原則(ASD) 
定義:類、模組、函式等等等應該是可以拓展的,但是不可修改。 
開放封閉有兩個含義,一個是對於拓展是開放的,另一個是對於修改是封閉的。對於開發來說需求肯定是要變化的,但是新需求一來,我們就要把類重新改一遍這顯然是令人頭疼的,所以我們設計程式時面對需求的改變要儘可能的保證相對的穩定,儘量用新程式碼實現拓展來修改需求,而不是通過修改原有的程式碼來實現。 
假設我們要實現一個列表,一開始只有查詢的功能,如果產品又要增加新增功能,過幾天又要增加刪除功能,大多數人的做法是寫個方法然後通過傳入不同的值來控制方法來實現不同的功能,但是如果又要新增功能我們還得修改我們的方法。用開發封閉原則解決就是增加一個抽象的功能類,讓增加和刪除和查詢的作為這個抽象功能類的子類,這樣如果我們再新增功能,你會發現我們不需要修改原有的類,只需要新增一個功能類的子類實現功能類的方法就可以了。

3.里氏替換原則(LSP) 
定義:所有引用基類(父類)的地方必須能透明地使用其子類的物件 
里氏代換原則告訴我們,在軟體中將一個基類物件替換成它的子類物件,程式將不會產生任何錯誤和異常,反過來則不成立,如果一個軟體實體使用的是一個子類物件的話,那麼它不一定能夠使用基類物件。 
里氏代換原則是實現開閉原則的重要方式之一,由於使用基類物件的地方都可以使用子類物件,因此在程式中儘量使用基類型別來對物件進行定義,而在執行時再確定其子類型別,用子類物件來替換父類物件。 
在使用里氏代換原則時需要注意如下幾個問題:

子類的所有方法必須在父類中宣告,或子類必須實現父類中宣告的所有方法。根據里氏代換原則,為了保證系統的擴充套件性,在程式中通常使用父類來進行定義,如果一個方法只存在子類中,在父類中不提供相應的宣告,則無法在以父類定義的物件中使用該方法。
我們在運用里氏代換原則時,儘量把父類設計為抽象類或者介面,讓子類繼承父類或實現父介面,並實現在父類中宣告的方法,執行時,子類例項替換父類例項,我們可以很方便地擴充套件系統的功能,同時無須修改原有子類的程式碼,增加新的功能可以通過增加一個新的子類來實現。里氏代換原則是開閉原則的具體實現手段之一。
Java語言中,在編譯階段,Java編譯器會檢查一個程式是否符合里氏代換原則,這是一個與實現無關的、純語法意義上的檢查,但Java編譯器的檢查是有侷限的。
4.依賴倒置原則(DIP) 
定義:高層模組不應該依賴低層模組,兩個都應該依賴於抽象。抽象不應該依賴於細節,細節應該依賴於抽象。 
在Java中,抽象就是指介面或者抽象類,兩者都是不能直接被例項化的;細節就是實現類,實現介面或者繼承抽象類而產生的就是細節,也就是可以加上一個關鍵字new產生的物件。高層模組就是呼叫端,低層模組就是具體實現類。 
依賴倒置原則在Java中的表現就是:模組間通過抽象發生,實現類之間不發生直接依賴關係,其依賴關係是通過介面或者抽象類產生的。如果類與類直接依賴細節,那麼就會直接耦合,那麼當修改時,就會同時修改依賴者程式碼,這樣限制了可擴充套件性。

5.迪米特原則(LOD) 
定義:一個軟體實體應當儘可能少地與其他實體發生相互作用。 
也稱為最少知識原則。如果一個系統符合迪米特法則,那麼當其中某一個模組發生修改時,就會盡量少地影響其他模組,擴充套件會相對容易,這是對軟體實體之間通訊的限制,迪米特法則要求限制軟體實體之間通訊的寬度和深度。迪米特法則可降低系統的耦合度,使類與類之間保持鬆散的耦合關係。 
迪米特法則要求我們在設計系統時,應該儘量減少物件之間的互動,如果兩個物件之間不必彼此直接通訊,那麼這兩個物件就不應當發生任何直接的相互作用,如果其中的一個物件需要呼叫另一個物件的某一個方法的話,可以通過第三者轉發這個呼叫。簡言之,就是通過引入一個合理的第三者來降低現有物件之間的耦合度。 
在將迪米特法則運用到系統設計中時,要注意下面的幾點:在類的劃分上,應當儘量建立鬆耦合的類,類之間的耦合度越低,就越有利於複用,一個處在鬆耦合中的類一旦被修改,不會對關聯的類造成太大波及;在類的結構設計上,每一個類都應當儘量降低其成員變數和成員函式的訪問許可權;在類的設計上,只要有可能,一個型別應當設計成不變類;在對其他類的引用上,一個物件對其他物件的引用應當降到最低。

6.介面隔離原則(ISP) 
定義:一個類對另一個類的依賴應該建立在最小的介面上。 
建立單一介面,不要建立龐大臃腫的介面,儘量細化介面,介面中的方法儘量少。也就是說,我們要為各個類建立專用的介面,而不要試圖去建立一個很龐大的介面供所有依賴它的類去呼叫。 
採用介面隔離原則對介面進行約束時,要注意以下幾點:

介面儘量小,但是要有限度。對介面進行細化可以提高程式設計靈活性,但是如果過小,則會造成介面數量過多,使設計複雜化。所以一定要適度。
為依賴介面的類定製服務,只暴露給呼叫的類它需要的方法,它不需要的方法則隱藏起來。只有專注地為一個模組提供定製服務,才能建立最小的依賴關係。
提高內聚,減少對外互動。使介面用最少的方法去完成最多的事情。

什麼是設計模式?

通俗來講,設計模式就是針對某一種特殊場景而給出的標準解決方案,它是前輩們的經驗性總結,也是實現軟體工程化的基礎,良好的設計模式應用 可以是我們的軟體變得更加健壯可維護。

設計模式按照型別劃分可以分為三大類,如下所示:

建立型設計模式:如同它的名字那樣,它是用來解耦物件的例項化過程。
結構型設計模式:將類和物件按照一定規則組合成一個更加強大的結構體。
行為型設計模式:定義類和物件的互動行為。
23種設計模式劃分如下圖所示:

 注:23種設計模式很多小夥伴都爛熟於心,但是真正程式設計實踐的時候未必會想的起來,這其實是一個潛移默化的過程,在看設計模式的時候,儘量多動手寫一寫,其中 手寫(不借助IDE)的效果最佳,可以加深理解,理解的深了,程式設計的時候自然就可以想的到去應用。

一 建立型設計模式
建立型設計模式主要用來解耦物件的例項化過程,控制例項的生成。

建立型設計模式一共有六種,如下所示:

1.1 單例模式
模式定義

當系統中只需要一個例項或者一個全域性訪問點的時候可以使用單例模式。

優點:節省系統建立物件的資源,提高了系統效率,提供了統一的訪問入口,可以嚴格控制使用者對該物件的訪問。
缺點:只有一個物件,積累的職責過重,違背了單一職責原則。構造方法為private,無法繼承,擴充套件性較差。
單例模式的實現由很多種,如下所示:

懶漢式單例
雙層校驗鎖單例
容器單例
靜態內部類單例
列舉單例
其中靜態內部類單例和列舉單例都是單例模式最佳的實現,但是出於便利性的考量,雙層校驗鎖的實現應用的更為廣泛,如下所示:

public class DoubleCheckSingleton {
    
    // volatile關鍵字保證了:① instance例項對於所有執行緒都是可見的 ② 禁止了instance 
    // 操作指令重排序。
    private volatile static DoubleCheckSingleton instance;

    private DoubleCheckSingleton() {
    }

    public static DoubleCheckSingleton getInstance() {
        // 第一次校驗,防止不必要的同步。
        if (instance == null) {
            // synchronized關鍵字加鎖,保證每次只有一個執行緒執行物件初始化操作
            synchronized (DoubleCheckSingleton.class) {
                // 第二次校驗,進行判空,如果為空則執行初始化
                if(instance == null){
                     instance = new DoubleCheckSingleton();
                }
            }
        }
        return instance;
    }
}
關於雙層校驗鎖單例為何能實現JVM單例,它的要點在於兩次判空和synchronized、volatile關鍵字,具體原理已經寫在上方的註釋裡,這裡 我們單獨說一下volatile關鍵字。

說明我們說到,volatile關鍵字禁止了instance 操作指令重排序,我們來解釋一下,我們知道instance = new DoubleCheckSingleton()這個操作 在彙編指令裡大致會做三件事情:

給我們知道instance分配記憶體。
呼叫DoubleCheckSingleton()構造方法。
將構造的物件賦值給instance。
但是在真正執行的時候,Java編譯器是允許指令亂序執行的(編譯優化),所以上述3步的順序得不到保證,有可能是132,試想一下,如果執行緒A沒有執行第2步,先執行了 第3步,而恰在此時,執行緒B取走了instance物件,在使用instance物件時就會有問題,雙層校驗鎖單例失敗,而volatile關鍵字可以禁止指令重排序從而解決這個問題。

單例模式的另一個問題就是多程序的情況下的失敗問題,因為JVN裡的單例是基於一個虛擬機器程序的,這個時候通常的做法就是讓這個單例支援跨程序呼叫,這個在 Android裡一般用AIDL實現。

1.2 建造者模式
模式定義

封裝一個複雜物件的構建過程,可以按照流程來構建物件。

優點:它可以將一個複雜物件的構建與表示相分離,同一個構建過程,可以構成出不同的產品,簡化了投建邏輯。
缺點:如果構建流程特別複雜,就是導致這個構建系統過於龐大,不利於控制。
建造者模式的實現,也十分簡單,如下所示:

public class Product {

    private String board;
    private String display;
    private String os;

    public String getBoard() {
        return board;
    }

    public String getDisplay() {
        return display;
    }

    public String getOs() {
        return os;
    }

    private Product(Builder builder) {
        // 進行構建
        this.board = builder.board;
        this.display = builder.display;
        this.os = builder.os;
    }

    public static class Builder {
        // 建造者模式還可以設定預設值
        private String board = "default value";
        private String display = "default value";
        private String os = "default value";

        public void setBoard(String board) {
            this.board = board;
        }

        public void setDisplay(String display) {
            this.display = display;
        }

        public void setOs(String os) {
            this.os = os;
        }


        public Product build() {
            return new Product(this);
        }
    }
}
1.3 原型模式
模式定義

當某個物件的資料結構或者構建過程特別複雜,頻繁的構建勢必會消耗系統性能,這個時候我們採用原型模式對原有的 物件進行克隆,構建新的物件。

優點:直接克隆原有例項生成新的例項,免去了複雜的構建過程,節省了系統資源。
缺點:
實現原型模式也很簡單,主需要宣告實現loneable介面,然後覆寫Object的clone()方法介面。

public class Person implements Cloneable{

    public int age;
    public String name;

    @Override
    public Person clone() throws CloneNotSupportedException {
        return (Person) super.clone();
    }
}
原型模式要注意深拷貝和淺拷貝的問題,Object的clone()方法預設是錢拷貝,即對於引用物件拷貝的地址而不是值,所以要實現 深拷貝,在clone()方法裡對於引用物件也有呼叫一下clone()方法,並且引用物件也要實現Cloneable介面和覆寫clone()方法。

接下來我們繼續看看三種工廠模式,如下所示:

簡單工廠模式:根據傳入的引數決定例項化哪個物件。
工廠模式:工廠模式定義了一個建立物件的介面,由子類進行物件的初始化,即工廠模式將子類的初始化推遲到了子類裡。
抽象工廠模式:抽象工廠模式和工廠模式很相似,只是它利用介面或者抽象類定義了一個產品族,例如定義一個撥號產品族,只定義功能,不 關心實現,具體實現交由Android、iOS等作業系統自己完成。
1.4 簡單工廠模式
模式定義

根據傳入的引數決定例項化哪個物件。

簡單工廠模式是工廠模式的簡化版本,無需定義抽象工廠,通常還可以利用反射來生成物件,簡化操作,如下所示:

// 簡單工廠
public class SimpleFactory {

    public static <T extends AbstractProduct> T create(Class<T> clasz) {
        AbstractProduct product = null;
        try {
            product = (AbstractProduct) Class.forName(clasz.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return (T) product;
    }
}
優點:
缺點:
1.5 工廠模式
模式定義

工廠模式定義了一個建立物件的介面,由子類進行物件的初始化,即工廠模式將子類的初始化推遲到了子類裡。抽象工廠模式

優點:工廠模式符合開閉原則,當需要增加一個新產品時,只需要增加一個具體產品類和一個具體工廠類,無需修改原有的系統,外界也無需 知道具體的產品類的實現。
缺點:每次增加新產品的時候都會增加產品類和工廠類,勢必會讓系統越來越龐大。
工廠模式的實現也很簡單,就是定義一個抽象類或者介面工廠,在子類工廠中決定例項化具體的類。

// 抽象工廠
public abstract class AbstractFactory {
    public abstract AbstractProduct create();
}

// 具體工廠
public class ConcretetFactory {

    public static AbstractProduct create() {
        return new ConcreteProductA();
//        return new ConcreteProductB();
    }
}

// 抽象產品
public class AbstractProduct {
}

// 具體產品A
public class ConcreteProductA extends AbstractProduct {
}

// 具體產品B
public class ConcreteProductB extends AbstractProduct {
}
1.6 抽象工廠模式
模式定義

抽象工廠模式和工廠模式很相似,只是它利用介面或者抽象類定義了一個產品族,例如定義一個撥號產品族,只定義功能,不 關心實現,具體實現交由Android、iOS等作業系統自己完成。

優點:
缺點:
實現如下所示:

// 抽象產品A
public abstract class AbstractProductA {
}

// 抽象產品B
public abstract class AbstractProductB {
}

// 具體產品A1
public class ConcreteProductA1 extends AbstractProductA{
}

// 具體產品A2
public class ConcreteProductA2 extends AbstractProductA {
}

// 具體產品B1
public class ConcreteProductB1 extends AbstractProductB {
}

// 具體產品B2
public class ConcreteProductB2 extends AbstractProductB {
}

// 抽象工廠
public abstract class AbstractFactory {
    
    public abstract AbstractProductA createProductA();
    public abstract AbstractProductB createProductB();
}

// 具體工廠
public class ConcreteFactory extends AbstractFactory {
    
    @Override
    public AbstractProductA createProductA() {
        return new ConcreteProductA1();
    }

    @Override
    public AbstractProductB createProductB() {
        return new ConcreteProductB1();
    }
}
二 組合型設計模式
2.1 介面卡模式
模式定義

介面卡模式可以將一個類的介面,轉換成客戶端期望的另一個介面,讓兩個原本不相容的介面可以無縫對接。

優點:
缺點:
// 目標介面
public interface TargetInterface {
    int getFive();
}

// 被適配物件
public class Adaptee {

    public int getTen() {
        return 10;
    }
}

// 介面卡
public class Adapter extends Adaptee implements TargetInterface {

    @Override
    public int getFive() {
        return 5;
    }
}
2.2 組合模式
模式定義

將物件組成樹形結構以表示整體-部分的層次結構,使得使用者對單個物件和組合物件的使用具有一致性。

應用場景

表示物件部分-整體的層次結構。
從一個整體中能夠獨立出部分模組或者功能的場景。
2.3 裝飾模式
模式定義

動態的為物件增加一些額外的功能。

應用場景

需要透明且動態的擴充套件類的功能時。
// 抽象元件類
public abstract class AbstractComponent {
    
    protected abstract void operation();
}

// 具體元件類
public class ConcreteComponent extends AbstractComponent {
    @Override
    protected void operation() {
        
    }
}

// 抽象裝飾類
public abstract class AbstractDecorator extends AbstractComponent {

    private AbstractComponent mComponent;

    public AbstractDecorator(AbstractComponent component) {
        mComponent = component;
    }

    @Override
    protected void operation() {
        mComponent.operation();
    }
}

// 具體裝飾類
public class ConcreteDecorator extends AbstractDecorator {

    public ConcreteDecorator(AbstractComponent component) {
        super(component);
    }

    @Override
    protected void operation() {
        operationA();
        super.operation();
        operationB();
    }

    private void operationA() {

    }

    private void operationB() {

    }
}
2.4 外觀模式
模式定義

要求一個字系統的外部與其內部的通訊都通過一個統一的而物件進行。

應用場景

子系統在迭代的過程中可以會不斷變化,甚至被替代掉,給一個統一的訪問介面,避免子系統的改變影響到外部的呼叫者。
當需要構建層次結構型的系統時,為各層子系統提供訪問的介面進行通訊,避免直接產生依賴。
// 列表介面
public interface List {
    
    void add();
    void remove();
    
}

// 陣列列表
public class ArrayList implements List {
    @Override
    public void add() {

    }

    @Override
    public void remove() {

    }
}

// 連結串列列表
public class LinkedList implements List {
    @Override
    public void add() {

    }

    @Override
    public void remove() {

    }
}
2.5 橋接模式
模式定義

將抽象部分和實現部分相互分離,使它們可以獨立變化。

應用場景

如果一個系統需要在抽象部分和實現部分增加更多的靈活性,避免兩種變化的時候相互影響。
如果不希望使用繼承而增加系統的複雜度,可以考慮使用橋接模式。
一個類存在兩個獨立變化的緯度,且這兩個緯度都希望進行擴充套件。
2.6 享元模式
模式定義

使用共享物件可以有效的支援大量的細粒度物件。

應用場景

系統中存在著大量的相似物件。
細粒度的物件都具有較接近的外部狀態,而且內部狀態與外部環境無關。
需要緩衝池的場景。
2.7 代理模式
模式定義

為其他物件提供一個代理以提供對這個物件的訪問。

應用場景

當無法或者不想直接訪問某個物件時,可以通過一個代理物件進行訪問。
代理模式按照代理類執行前是否存在還可以分為靜態代理和動態代理,如下所示:

靜態代理

// 被代理介面,定義要實現的功能。
public interface Subject {

    void visit();
}

// 被代理類,完成實際的功能。
public class ConcreteSubject implements Subject {

    @Override
    public void visit() {
        System.out.println("visit");
    }
}

// 靜態代理類,與被代理類實現同一套介面
public class StaticProxy implements Subject {

    private Subject mSubject;

    public StaticProxy(Subject subject) {
        mSubject = subject;
    }

    @Override
    public void visit() {
        mSubject.visit();
    }
}
動態代理

// 被代理介面,定義要實現的功能。
public interface Subject {

    void visit();
}

// 被代理類,完成實際的功能。
public class ConcreteSubject implements Subject {

    @Override
    public void visit() {
        System.out.println("visit");
    }
}

// 動態代理類,實現InvocationHandler介面。
public class DynamicProxy implements InvocationHandler {

    private Subject mSubject;

    public DynamicProxy(Subject subject) {
        mSubject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("函式執行前自定義操作");
        // 呼叫被代理類的方法時會呼叫該方法
        method.invoke(mSubject, args);
        System.out.println("函式執行後自定義操作");
        return null;
    }
}

public class Client {

    public static void main(String[] args) {

        Subject subject = new ConcreteSubject();
        DynamicProxy proxy = new DynamicProxy(subject);

        // 動態生成代理類
        Subject proxySubject = (Subject) Proxy.newProxyInstance(DynamicProxy.class.getClassLoader()
                , subject.getClass().getInterfaces()
                , proxy);
        proxySubject.visit();

    }
}
三 行為型設計模式
3.1 模板模式
模式定義

定義一個操作的演算法框架,而將具體實現延遲到子類中進行,使得子類在不改變整體演算法框架的基礎上,可以自定義演算法實現。

應用場景

多個子類有公有的方法,並且邏輯基本相同時。
重要複雜的演算法可以把核心演算法設計為模板方法,具體細節則由子類實現
重構程式碼時,把相同的程式碼抽取到父類中,然後通過鉤子函式約束其行為。
3.2 直譯器模式
模式定義

給定一個語音,定義它的文法的一種表示,並定義一個直譯器。

應用場景

如果某個簡單的語音需要解釋執行並且可以將該語言中的語句表示為一個抽象語法樹時可以使用直譯器模式。
在某些特定領域不斷出現的問題是,可以將該領域的問題轉船為一種語法規則下的語句,然後構建直譯器來解釋該語句。
3.3 策略模式
模式定義

策略模式定義了一系列演算法,並將演算法封裝起來可以互相替換,策略模式讓演算法與使用它的客戶端解耦,可以獨立變化。

應用場景

針對同一型別的問題有多種處理方式,僅僅是具體的行為有差別時。
需要安全的封裝多種同一型別的操作時。
出現同一抽象類的多個字類,而又需要使用if-else來選擇子類時。
策略模式的實現也非常簡單,依賴於介面或者抽象類,如下所示:

// 策略介面,定義功能。
public interface IStrategy {
    void method();
}

// 策略A
public class StrategyA implements IStrategy {
    @Override
    public void method() {

    }
}

// 策略B
public class StrategyB implements IStrategy {
    @Override
    public void method() {
        
    }
}
3.4 狀態模式
模式定義

允許一個物件在內部狀態改變時改變它的行為。

應用場景

一個物件的行為取決於它的狀態,並且它在執行時根據狀態改變它的行為。
程式碼中包含大量與物件狀態相關的判斷語句。
狀態模式的核心是根據不同的狀態對應不同的操作,如下所示:

// 操作介面
public interface TVState {
    void nextChannel();

    void lastChannel();
}

// 開機狀態
public class PowerOnState implements TVState {

    @Override
    public void nextChannel() {
        // next channel 
    }

    @Override
    public void lastChannel() {
        // last channel
    }
}

// 關機狀態
public class PowerOffChannel implements TVState {
    @Override
    public void nextChannel() {
        // do nothing
    }

    @Override
    public void lastChannel() {
        // do nothing
    }
}
3.5 觀察者模式
模式定義

定義物件間一對多的依賴關係,每當這個物件發生改變時,其他物件都能收到通知並更新自己。

應用場景

關聯行為場景
事件多級觸發場景
跨系統的訊息交換場景,例如訊息佇列,事件匯流排的處理機制。
觀察者模式的實現如下所示:

// 監聽介面,通知被監聽物件發生改變
public interface Listener {

    void change();
}

// 被監聽者
public class Observable {

    private Listener mListener;

    // 設定監聽器
    public void setListener(Listener listener) {
        mListener = listener;
    }

    public void onChange() {
        // 通知物件發生改變
        mListener.change();
    }
}

// 監聽者
public class Observer {

    public void setup() {
        Observable observable = new Observable();
        observable.setListener(new Listener() {
            @Override
            public void change() {
                // TODO 監聽的物件發生改變
            }
        });
    }
}
3.6 備忘錄模式
模式定義

在不破壞封裝的前提下,儲存一個物件的內部狀態,並在該物件之外儲存這個狀態,以便可以將該物件恢復到儲存時的狀態。

應用場景

需要儲存某個物件在某一時刻的狀態。
外界向訪問物件的狀態,但是又不想直接暴露介面給外部,這時候可以將物件狀態儲存下來,間接的暴露給外部。
3.7 中介者模式
模式定義

中介者模式定義了一系列物件間的互動方式,使得這些物件像話作用而又不耦合在一起。

應用場景

當物件間有很多的互動操作,而且一個物件的行為依賴於其他物件時,可以利用中介者模式解決緊耦合的問題。
3.8 命令模式
模式定義

將一個請求封裝成一個物件,可以將不同的請求引數化,可以對請求就行排隊、日誌記錄以及撤銷等操作。

應用場景

需要抽象待執行的動作,然後以引數的形式提供出來。
在不同的時刻,指定和排列請求。
需要支援撤銷操作。
需要支援日誌功能,這樣當系統崩潰時,可以重做一遍。
需要支援事務操作。
命令模式的實現如下所示:

// 接收命令
public class Receiver {

    public void action() {
        // TODO 真正執行命令具體邏輯
    }
}

// 抽象命令
public interface AbstractCommand {
    
    // 執行命令
    void command();
}

// 具體命令
public class ConcreteCommand implements AbstractCommand {

    private Receiver mReceiver;

    public ConcreteCommand(Receiver receiver) {
        mReceiver = receiver;
    }

    @Override
    public void command() {
        mReceiver.action();
    }
}

// 呼叫者
public class Invoker {

    private AbstractCommand mCommmand;

    public Invoker(AbstractCommand command) {
        mCommmand = command;
    }

    public void invoke() {
        mCommmand.command();
    }
}
3.9 訪問者模式
模式定義

封裝一些作用於某些資料結構中的各元素的操作,它可以在不改變資料結構的前提下賦予這些元素新的操作。

應用場景

物件結構比較穩定,但是需要在物件結構的基礎上定義新的操作。
需要對同一個類的不同物件執行不不同的操作,但是不希望增加操作的時候改變這些類。
3.10 責任鏈模式
模式定義

將請求的傳送者和接收者進行解耦,使得多個物件都有機會處理該請求,將這些物件串成一條鏈,並沿著這條鏈子處理請求,直到有物件處理它為止。

應用場景

多個物件可以處理同一個請求,但是具體由哪個物件處理在執行時動態決定。
在請求處理者不明確的情況下向多個物件中的一個提交請求。
需要動態指定一組物件的處理請求。
責任鏈模式的實現主要在於處理器的迭代,要麼使用迴圈迭代,要麼使用連結串列後繼,如下所示:

// 處理器,定位行為和下一個處理器
public abstract class Handler {
    protected Handler next;

    public abstract void handleRequest(String condition);
}

// 處理器1
public class Handler1 extends Handler {
    @Override
    public void handleRequest(String condition) {
        if (TextUtils.equals(condition, "Handler1")) {
            // process request
        } else {
            // next handler
            next.handleRequest(condition);
        }
    }
}

// 處理器2
public class Handler2 extends Handler {
    
    @Override
    public void handleRequest(String condition) {
        if (TextUtils.equals(condition, "Handler2")) {
            // process request
        } else {
            // next handler
            next.handleRequest(condition);
        }
    }
}
3.11 迭代器模式
模式定義

提供一個方法順序訪問一個容器內的元素,而又不暴露該物件的內部表示。

應用場景

遍歷一個容器時。
--------------------- 

原文:https://blog.csdn.net/wy391920778/article/details/79621941