快速梳理常用的設計模式(中篇)

qqxx6661發表於2019-02-27

前言

本文旨在快速梳理常用的設計模式,瞭解每個模式主要針對的是哪些情況以及其基礎特徵,每個模式前都有列舉出一個或多個可以深入閱讀的參考網頁,以供讀者詳細瞭解其實現。

分為三篇文章:

  • 上篇:設計模式基礎理念和建立型設計模式
  • 中篇:行為型設計模式
  • 下篇:結構型設計模式

面試知識點複習手冊

全複習手冊文章導航

點選公眾號下方:技術推文——面試衝刺

全複習手冊文章導航(CSDN)

快速回憶

行為型

  • 責任鏈(Chain Of Responsibility)
  • 命令(Command)
  • 直譯器(Interpreter)
  • 迭代器(Iterator)
  • 中介者(Mediator)
  • 備忘錄(Memento)
  • 觀察者(Observer)
  • 狀態(State)
  • 策略(Strategy)
  • 模板方法(Template Method)
  • 訪問者(Visitor)
  • 空物件(Null)

理念

首先搞清楚一點,設計模式不是高深技術,不是奇淫技巧。設計模式只是一種設計思想,針對不同的業務場景,用不同的方式去設計程式碼結構,其最最本質的目的是為了解耦,延伸一點的話,還有為了可擴充套件性和健壯性,但是這都是建立在解耦的基礎之上。

高內聚低耦合

高內聚:系統中A、B兩個模組進行互動,如果修改了A模組,不影響模組B的工作,那麼認為A有足夠的內聚。

低耦合:就是A模組與B模組存在依賴關係,那麼當B發生改變時,A模組仍然可以正常工作,那麼就認為A與B是低耦合的。

行為型

責任鏈(Chain Of Responsibility)

blog.csdn.net/maoyuanming…

意圖

使多個物件都有機會處理請求,從而避免請求的傳送者和接收者之間的耦合關係。將這些物件連成一條鏈,並沿著這條鏈傳送該請求,直到有一個物件處理它為止。

在這種模式中,通常每個接收者都包含對另一個接收者的引用。如果一個物件不能處理該請求,那麼它會把相同的請求傳給下一個接收者,依此類推。

職責和角色

  • Handler:處理類的抽象父類
  • concreteHandler:具體的處理類

設計使用責任鏈的基本流程

  • 組織物件鏈:將某人物的所有職責執行物件以鏈的形式加以組織起來
  • 訊息或請求的傳遞:將訊息或請求沿著物件鏈傳遞,讓處於物件鏈中的物件得到處理機會
  • 物件鏈中物件的職責分配:不同物件完成不同職責
  • 任務的完成:物件鏈末尾的物件結束任務並停止訊息或請求的繼續傳遞。

應用例項

紅樓夢中的"擊鼓傳花"。 JS 中的事件冒泡。 JAVA WEB 中 Apache Tomcat 對 Encoding 的處理,Struts2 的攔截器,jsp servlet 的 Filter,springMVC的攔截器

何時使用:在處理訊息的時候以過濾很多道。

如何解決:攔截的類都實現統一介面。

關鍵程式碼:Handler 裡面聚合它自己,在 HandlerRequest 裡判斷是否合適,如果沒達到條件則向下傳遞,向誰傳遞之前 set 進去。

實現舉例

在這裡插入圖片描述

處理器的抽象類

package com.mym.designmodel.CoRModel;

/**
 * 職責:Handler 職責類的抽象父類
 */
public abstract class AbstractCarHandler {

    AbstractCarHandler carHandler = null;

    public abstract void carHandler();

    public AbstractCarHandler setNextCarHandler(AbstractCarHandler nextCarHandler){
        this.carHandler = nextCarHandler;
        return this.carHandler;
    }

    /**職責下傳*/
    protected void doChain(){
        if(this.carHandler != null){
            this.carHandler.carHandler();
        }
    }
}
複製程式碼

責任鏈一個執行者1

package com.mym.designmodel.CoRModel;

/**
 * 職責:concreteHandler 具體的處理類
 */
public class CarHeadHandler extends AbstractCarHandler {
    @Override
    public void carHandler() {
        System.out.println("處理車的head!");

        //下傳
        this.doChain();
    }
}
複製程式碼

責任鏈一個執行者2

package com.mym.designmodel.CoRModel;

/**
 * 職責:concreteHandler 具體的處理類
 */
public class CarBodyHandler extends AbstractCarHandler {
    @Override
    public void carHandler() {
        System.out.println("處理車的body!");

        //下傳
        this.doChain();
    }
}
複製程式碼

責任鏈一個執行者3

package com.mym.designmodel.CoRModel;

/**
 * 職責:concreteHandler 具體的處理類
 */
public class CarTailHandler extends AbstractCarHandler {
    @Override
    public void carHandler() {
        System.out.println("處理車的tail!");

        //下傳
        this.doChain();
    }
}
複製程式碼

客戶端client

package com.mym.designmodel.CoRModel;

/**
 * 測試
 */
public class MainClass {

    public static void main(String[] args) {
        AbstractCarHandler carheadHandle = new CarHeadHandler();
        AbstractCarHandler carbodyHandle = new CarBodyHandler();
        AbstractCarHandler carTailHandler = new CarTailHandler();

        //組裝責任鏈
        carheadHandle.setNextCarHandler(carbodyHandle).setNextCarHandler(carTailHandler);

        //鏈頭部開始執行
        carheadHandle.carHandler();
    }
}
複製程式碼

JDK

命令(Command)

www.cnblogs.com/konck/p/419…

意圖

命令模式是為了解決命令的請求者和命令的實現者之間的耦合關係。

解決了這種耦合的好處我認為主要有兩點:

1.更方便的對命令進行擴充套件(注意:這不是主要的優勢,後面會提到)

2.對多個命令的統一控制(這種控制包括但不限於:佇列、撤銷/恢復、記錄日誌等等)

應用例項

struts 1 中的 action 核心控制器 ActionServlet 只有一個,相當於 Invoker,而模型層的類會隨著不同的應用有不同的模型類,相當於具體的 Command。

類圖

  • Command:命令
  • Receiver:命令接收者,也就是命令真正的執行者
  • Invoker:通過它來呼叫命令
  • Client:可以設定命令與命令的接收者

JDK

直譯器(Interpreter)

www.cnblogs.com/chenssy/p/3…

意圖

所謂直譯器模式就是定義語言的文法,並且建立一個直譯器來解釋該語言中的句子。

這種模式實現了一個表示式介面,該介面解釋一個特定的上下文。這種模式被用在 SQL 解析、符號處理引擎等。

應用例項

編譯器、運算表示式計算。

JDK

迭代器(Iterator)

www.jianshu.com/p/3d0406a01…

意圖

提供一種順序訪問聚合物件元素的方法,並且不暴露聚合物件的內部表示。

迭代器模式的優缺點

優點

①簡化了遍歷方式,對於物件集合的遍歷,還是比較麻煩的,對於陣列或者有序列表,我們尚可以通過遊標來取得,但使用者需要在對集合瞭解很清楚的前提下,自行遍歷物件,但是對於hash表來說,使用者遍歷起來就比較麻煩了。而引入了迭代器方法後,使用者用起來就簡單的多了。

②可以提供多種遍歷方式,比如說對有序列表,我們可以根據需要提供正序遍歷,倒序遍歷兩種迭代器,使用者用起來只需要得到我們實現好的迭代器,就可以方便的對集合進行遍歷了。

③封裝性良好,使用者只需要得到迭代器就可以遍歷,而對於遍歷演算法則不用去關心。

缺點

對於比較簡單的遍歷(像陣列或者有序列表),使用迭代器方式遍歷較為繁瑣,大家可能都有感覺,像ArrayList,我們寧可願意使用for迴圈和get方法來遍歷集合。

JDK

中介者(Mediator)

www.runoob.com/design-patt…

意圖

集中相關物件之間複雜的溝通和控制方式。

實現

Alarm(鬧鐘)、CoffeePot(咖啡壺)、Calendar(日曆)、Sprinkler(噴頭)是一組相關的物件,在某個物件的事件產生時需要去操作其它物件,形成了下面這種依賴結構:

在這裡插入圖片描述

使用中介者模式可以將複雜的依賴結構變成星形結構:

在這裡插入圖片描述

JDK

備忘錄(Memento)

blog.csdn.net/o279642707/…

意圖

在不違反封裝的情況下獲得物件的內部狀態,從而在需要時可以將物件恢復到最初狀態。

主要解決

所謂備忘錄模式就是在不破壞封裝的前提下,捕獲一個物件的內部狀態,並在該物件之外儲存這個狀態,這樣可以在以後將物件恢復到原先儲存的狀態。

應用例項

1、後悔藥。 2、打遊戲時的存檔。 3、Windows 裡的 ctri + z。 4、IE 中的後退。 5、資料庫的事務管理。

JDK

  • java.io.Serializable

觀察者(Observer)

www.jianshu.com/p/fc4554cda…

意圖

觀察者模式是物件的行為模式,又叫釋出-訂閱(Publish/Subscribe)模式、模型-檢視(Model/View)模式、源-監聽器(Source/Listener)模式或從屬者(Dependents)模式。

觀察者模式定義了一種一對多的依賴關係,讓多個觀察者物件同時監聽某一個主題物件。這個主題物件在狀態上發生變化時,會通知所有觀察者物件,使它們能夠自動更新自己。

在這裡插入圖片描述

何時使用

一個物件(目標物件)的狀態發生改變,所有的依賴物件(觀察者物件)都將得到通知,進行廣播通知。

實現舉例

天氣資料佈告板會在天氣資訊發生改變時更新其內容,佈告板有多個,並且在將來會繼續增加。

在這裡插入圖片描述

JDK

狀態(State)

www.cnblogs.com/java-my-lif…

意圖

允許物件在內部狀態改變時改變它的行為,物件看起來好像修改了它所屬的類。

應用例項

考慮一個線上投票系統的應用,要實現控制同一個使用者只能投一票,如果一個使用者反覆投票,而且投票次數超過5次,則判定為惡意刷票,要取消該使用者投票的資格,當然同時也要取消他所投的票;如果一個使用者的投票次數超過8次,將進入黑名單,禁止再登入和使用系統。

策略(Strategy)

www.jianshu.com/p/7fa8ad000…

www.cnblogs.com/wenjiang/p/…

意圖

定義一系列演算法,封裝每個演算法,並使它們可以互換。

策略模式和狀態模式的區別:

之所以說狀態模式是策略模式的孿生兄弟,是因為它們的UML圖是一樣的,但意圖卻完全不一樣,**策略模式是讓使用者指定更換的策略演算法,而狀態模式是狀態在滿足一定條件下的自動更換,使用者無法指定狀態,最多隻能設定初始狀態。 **

策略模式可以讓演算法獨立於使用它的客戶端。

具體場景實現

假設現在要設計一個販賣各類書籍的電子商務網站的購物車系統。一個最簡單的情況就是把所有貨品的單價乘上數量,但是實際情況肯定比這要複雜。比如,本網站可能對所有的高階會員提供每本20%的促銷折扣;對中級會員提供每本10%的促銷折扣;對初級會員沒有折扣。

根據描述,折扣是根據以下的幾個演算法中的一個進行的:

演算法一:對初級會員沒有折扣。

演算法二:對中級會員提供10%的促銷折扣。

演算法三:對高階會員提供20%的促銷折扣。

public static void main(String[] args) {
        //選擇並建立需要使用的策略物件
        MemberStrategy strategy = new AdvancedMemberStrategy();
        //建立環境
        Price price = new Price(strategy);
        //計算價格
        double quote = price.quote(300);
        System.out.println("圖書的最終價格為:" + quote);
    }
複製程式碼

策略模式對多型的使用

通過讓環境類持有一個抽象策略類(超類)的引用,在生成環境類例項物件時,讓該引用指向具體的策略子類。再對應的方法呼叫中,就會通過Java的多型,呼叫對應策略子類的方法。從而可以相互替換,不需要修改環境類內部的實現。同時,在有新的需求的情況下,也只需要修改策略類即可,降低與環境類之間的耦合度。

策略模式和工廠方法的異同

工廠模式和策略模式的區別在於例項化一個物件的位置不同,對工廠模式而言,例項化物件是放在服務端的,即放在了工廠類裡面; 而策略模式例項化物件的操作在客戶端

工廠模式要求服務端的銷售部門足夠靈敏,而策略模式由於對策略進行了封裝,所以他的銷售部門比較傻,需要客戶提供足夠能區分使用哪種策略的引數,而這最好的就是該策略的例項了。

JDK

  • java.util.Comparator#compare()
  • javax.servlet.http.HttpServlet
  • javax.servlet.Filter#doFilter()

模板方法(Template Method)

www.jianshu.com/p/cc391b56b…

典型用例:Spring

定義

模板方法模式是類的行為模式。

準備一個抽象類,將部分邏輯以具體方法以及具體建構函式的形式實現,然後宣告一些抽象方法來迫使子類實現剩餘的邏輯。不同的子類可以以不同的方式實現這些抽象方法,從而對剩餘的邏輯有不同的實現。這就是模板方法模式的用意。

結構

模板方法中的方法可以分為兩大類:模板方法和基本方法。

模板方法

一個模板方法是定義在抽象類中的,把基本操作方法組合在一起形成一個總演算法或一個總行為的方法。

一個抽象類可以有任意多個模板方法,而不限於一個。每一個模板方法都可以呼叫任意多個具體方法。

基本方法

基本方法又可以分為三種

  • 抽象方法:一個抽象方法由抽象類宣告,由具體子類實現。在Java語言裡抽象方法以abstract關鍵字標示。

  • 具體方法:一個具體方法由抽象類宣告並實現,而子類並不實現或置換。

  • 鉤子方法:一個鉤子方法由抽象類宣告並實現,而子類會加以擴充套件。通常抽象類給出的實現是一個空實現,作為方法的預設實現。

預設鉤子方法

一個鉤子方法常常由抽象類給出一個空實現作為此方法的預設實現。這種空的鉤子方法叫做“Do Nothing Hook”。具體模版類中可以選擇是否重寫鉤子方法,通常重寫鉤子方法是為了對模版方法中的步驟進行控制,判斷鉤子方法中的狀態,是否進行下一步操作。

使用場景

模板方法模式是基於繼承的程式碼複用技術,它體現了物件導向的諸多重要思想,是一種使用較為頻繁的模式。模板方法模式廣泛應用於框架設計中,以確保通過父類來控制處理流程的邏輯順序(如框架的初始化,測試流程的設定等)。

JDK

  • java.util.Collections#sort()
  • java.io.InputStream#skip()
  • java.io.InputStream#read()
  • java.util.AbstractList#indexOf()

訪問者(Visitor)

www.jianshu.com/p/80b9cd7c0…

什麼是訪問者模式?

比如我有一個賬單,賬單有收入,支出兩個固定方法。但是訪問賬單的人不確定,有可能是一個或者多個。

訪問者模式有兩個特點

一般被訪問的東西所持有的方法是固定的,就像賬單隻有收入和支出兩個功能。而訪問者是不固定的。

資料操作與資料結構相分離:頻繁的更改資料,但不結構不變。比如:雖然每一天賬單的資料都會變化(資料變化),但是隻有兩類資料,就是支出和收入(結構不變)。

程式碼

見參考網頁

空物件(Null)

意圖

使用什麼都不做的空物件來代替 NULL。

一個方法返回 NULL,意味著方法的呼叫端需要去檢查返回值是否是 NULL,這麼做會導致非常多的冗餘的檢查程式碼。並且如果某一個呼叫端忘記了做這個檢查返回值,而直接使用返回的物件,那麼就有可能丟擲空指標異常。

-----正文結束-----

更多精彩文章,請查閱我的部落格或關注我的公眾號:Rude3Knife

全複習手冊文章導航

點選公眾號下方:技術推文——面試衝刺

全複習手冊文章導航(CSDN)

知識點複習手冊文章推薦

關注我

我是蠻三刀把刀,目前為後臺開發工程師。主要關注後臺開發,網路安全,Python爬蟲等技術。

來微信和我聊聊:yangzd1102

Github:github.com/qqxx6661

原創部落格主要內容

  • 筆試面試複習知識點手冊
  • Leetcode演算法題解析(前150題)
  • 劍指offer演算法題解析
  • Python爬蟲相關技術分析和實戰
  • 後臺開發相關技術分析和實戰

同步更新以下部落格

1. Csdn

blog.csdn.net/qqxx6661

擁有專欄:Leetcode題解(Java/Python)、Python爬蟲開發、面試助攻手冊

2. 知乎

www.zhihu.com/people/yang…

擁有專欄:碼農面試助攻手冊

3. 掘金

juejin.im/user/5b4801…

4. 簡書

www.jianshu.com/u/b5f225ca2…

個人公眾號:Rude3Knife

個人公眾號:Rude3Knife

如果文章對你有幫助,不妨收藏起來並轉發給您的朋友們~

相關文章