快速梳理23種常用的設(shè)計模式(上)(創(chuàng)建型+行為型)
時間:2023-09-04 03:42:02 | 來源:網(wǎng)站運營
時間:2023-09-04 03:42:02 來源:網(wǎng)站運營
快速梳理23種常用的設(shè)計模式(上)(創(chuàng)建型+行為型):本站有篇幅限制,如需要更好的閱讀體驗,請點擊本文CSDN地址:快速梳理23種常用的設(shè)計模式 - Rude3Knife的博客 - CSDN博客
本文旨在快速梳理常用的設(shè)計模式,了解每個模式主要針對的是哪些情況以及其基礎(chǔ)特征,每個模式前都有列舉出一個或多個可以深入閱讀的參考網(wǎng)頁,以供讀者詳細(xì)了解其實現(xiàn)。快速回憶
一、創(chuàng)建型
- 單例(Singleton)
- 工廠模式
- 簡單工廠(Simple Factory)
- 工廠方法(Factory Method)
- 抽象工廠(Abstract Factory)
- 生成器(Builder)
- 原型模式(Prototype)
二、行為型
- 責(zé)任鏈(Chain Of Responsibility)
- 命令(Command)
- 解釋器(Interpreter)
- 迭代器(Iterator)
- 中介者(Mediator)
- 備忘錄(Memento)
- 觀察者(Observer)
- 狀態(tài)(State)
- 策略(Strategy)
- 模板方法(Template Method)
- 訪問者(Visitor)
三、結(jié)構(gòu)型
- 適配器(Adapter)
- 裝飾器(Decorator)
- 代理模式(Proxy)
- 外觀模式/門面模式(Facade)
- 橋接模式(Bridge Pattern)
- 組合模式(Composite)
- 享元模式(Flyweight)
理念
首先搞清楚一點,設(shè)計模式不是高深技術(shù),不是奇淫技巧。設(shè)計模式只是一種設(shè)計思想,針對不同的業(yè)務(wù)場景,用不同的方式去設(shè)計代碼結(jié)構(gòu),
其最最本質(zhì)的目的是為了解耦,延伸一點的話,
還有為了可擴展性和健壯性,但是這都是建立在解耦的基礎(chǔ)之上。
高內(nèi)聚低耦合
高內(nèi)聚:系統(tǒng)中A、B兩個模塊進行交互,如果修改了A模塊,不影響模塊B的工作,那么認(rèn)為A有足夠的內(nèi)聚。
image.png
低耦合:就是A模塊與B模塊存在依賴關(guān)系,那么當(dāng)B發(fā)生改變時,A模塊仍然可以正常工作,那么就認(rèn)為A與B是低耦合的。
image.png
創(chuàng)建型
單例模式
請參考Github詳細(xì)解釋,下面的一點僅供快速復(fù)習(xí)。Github寫的很好。同時參考:
http://blog.jobbole.com/109449/意圖
確保一個類只有一個實例,并提供該實例的全局訪問點。
類圖
使用一個私有構(gòu)造函數(shù)、一個私有靜態(tài)變量以及一個公有靜態(tài)函數(shù)來實現(xiàn)。私有構(gòu)造函數(shù)保證了不能通過構(gòu)造函數(shù)來創(chuàng)建對象實例,只能通過公有靜態(tài)函數(shù)返回唯一的私有靜態(tài)變量。
實現(xiàn)
懶漢式——線程安全/雙重校驗
單例是為了保證系統(tǒng)中只有一個實例,其關(guān)鍵點有
一.私有化構(gòu)造函數(shù)二.聲明靜態(tài)單例對象三.構(gòu)造單例對象之前要加鎖(lock一個靜態(tài)的object對象)或者方法上加synchronized。
四.需要兩次檢測單例實例是否已經(jīng)被構(gòu)造,分別在鎖之前和鎖之后
使用lock(obj)
public class Singleton { private Singleton() {} //關(guān)鍵點0:構(gòu)造函數(shù)是私有的 private volatile static Singleton single; //關(guān)鍵點1:聲明單例對象是靜態(tài)的 private static object obj= new object(); public static Singleton GetInstance() //通過靜態(tài)方法來構(gòu)造對象 { if (single == null) //關(guān)鍵點2:判斷單例對象是否已經(jīng)被構(gòu)造 { lock(obj) //關(guān)鍵點3:加線程鎖 { if(single == null) //關(guān)鍵點4:二次判斷單例是否已經(jīng)被構(gòu)造 { single = new Singleton(); } } } return single; } }
使用synchronized (Singleton.class)
public class Singleton { private Singleton() {} private volatile static Singleton uniqueInstance; public static Singleton getUniqueInstance() { if (uniqueInstance == null) { synchronized (Singleton.class) { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } } } return uniqueInstance; }}
可能提問
0.為何要檢測兩次?
如果兩個線程同時執(zhí)行 if 語句,那么兩個線程就會同時進入 if 語句塊內(nèi)。雖然在if語句塊內(nèi)有加鎖操作,但是兩個線程都會執(zhí)行 uniqueInstance = new Singleton(); 這條語句,只是先后的問題,也就是說會進行兩次實例化,從而產(chǎn)生了兩個實例。因此必須使用雙重校驗鎖,也就是需要使用兩個 if 語句。
1.構(gòu)造函數(shù)能否公有化?不行,單例類的構(gòu)造函數(shù)必須私有化,單例類不能被實例化,單例實例只能靜態(tài)調(diào)用。
2.lock住的對象為什么要是object對象,可以是int嗎?不行,
鎖住的必須是個引用類型。如果鎖值類型,每個不同的線程在聲明的時候值類型變量的地址都不一樣,那么上個線程鎖住的東西下個線程進來會認(rèn)為根本沒鎖。
3.uniqueInstance 采用 volatile 關(guān)鍵字修飾uniqueInstance = new Singleton(); 這段代碼其實是分為三步執(zhí)行。
分配內(nèi)存空間初始化對象將 uniqueInstance 指向分配的內(nèi)存地址
但是由于 JVM 具有指令重排的特性,有可能執(zhí)行順序變?yōu)榱?
1>3>2public class Singleton { private volatile static Singleton uniqueInstance; private Singleton(){} public static Singleton getInstance(){ if(uniqueInstance == null){ // B線程檢測到uniqueInstance不為空 synchronized(Singleton.class){ if(uniqueInstance == null){ uniqueInstance = new Singleton(); // A線程被指令重排了,剛好先賦值了;但還沒執(zhí)行完構(gòu)造函數(shù)。 } } } return uniqueInstance;// 后面B線程執(zhí)行時將引發(fā):對象尚未初始化錯誤。 }}
餓漢式-線程安全
線程不安全問題主要是由于 uniqueInstance 被實例化了多次,如果 uniqueInstance 采用直接實例化的話,就不會被實例化多次,也就不會產(chǎn)生線程不安全問題。但是直接實例化的方式也丟失了延遲實例化帶來的節(jié)約資源的優(yōu)勢。private static Singleton uniqueInstance = new Singleton();
靜態(tài)內(nèi)部類實現(xiàn)
當(dāng) Singleton 類加載時,靜態(tài)內(nèi)部類 SingletonHolder 沒有被加載進內(nèi)存。只有當(dāng)調(diào)用 getUniqueInstance()
方法從而觸發(fā) SingletonHolder.INSTANCE
時 SingletonHolder 才會被加載,此時初始化 INSTANCE 實例。這種方式不僅具有延遲初始化的好處,而且由虛擬機提供了對線程安全的支持。
public class Singleton { private Singleton() {} private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getUniqueInstance() { return SingletonHolder.INSTANCE; }}
枚舉實現(xiàn)
這是單例模式的最佳實踐,它實現(xiàn)簡單,并且在面對復(fù)雜的序列化或者反射攻擊的時候,能夠防止實例化多次。
public enum Singleton { uniqueInstance;}
考慮以下單例模式的實現(xiàn),該 Singleton 在每次序列化的時候都會創(chuàng)建一個新的實例,為了保證只創(chuàng)建一個實例,必須聲明所有字段都是 transient,并且提供一個 readResolve() 方法。
public class Singleton implements Serializable { private static Singleton uniqueInstance; private Singleton() { } public static synchronized Singleton getUniqueInstance() { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; }}
如果不使用枚舉來實現(xiàn)單例模式,會出現(xiàn)反射攻擊,因為通過 setAccessible() 方法可以將私有構(gòu)造函數(shù)的訪問級別設(shè)置為 public,然后調(diào)用構(gòu)造函數(shù)從而實例化對象。如果要防止這種攻擊,需要在構(gòu)造函數(shù)中添加防止實例化第二個對象的代碼。
從上面的討論可以看出,解決序列化和反射攻擊很麻煩,而枚舉實現(xiàn)不會出現(xiàn)這兩種問題,所以說枚舉實現(xiàn)單例模式是最佳實踐。使用場景
- Logger Classes
- Configuration Classes
- Accesing resources in shared mode
- Factories implemented as Singletons
簡單/靜態(tài)工廠(Simple Factory)
https://www.jianshu.com/p/d1b6731c1c0e定義
在創(chuàng)建一個對象時不向客戶暴露內(nèi)部細(xì)節(jié),并提供一個創(chuàng)建對象的通用接口。
在簡單工廠模式中,
可以根據(jù)參數(shù)的不同返回不同類的實例。
簡單工廠模式
專門定義一個類來負(fù)責(zé)創(chuàng)建其他類的實例結(jié)構(gòu)
簡單工廠模式包含如下角色:
- Factory:工廠角色
工廠角色負(fù)責(zé)實現(xiàn)創(chuàng)建所有實例的內(nèi)部邏輯 - Product:抽象產(chǎn)品角色
抽象產(chǎn)品角色是所創(chuàng)建的所有對象的父類,負(fù)責(zé)描述所有實例所共有的公共接口 - ConcreteProduct:具體產(chǎn)品角色
具體產(chǎn)品角色是創(chuàng)建目標(biāo),所有創(chuàng)建的對象都充當(dāng)這個角色的某個具體類的實例。
實現(xiàn)
public class Test { public static void main(String[] args) { String loginType = "password"; String name = "name"; String password = "password"; Login login = LoginManager.factory(loginType); boolean bool = login.verify(name, password); if (bool) { /** * 業(yè)務(wù)邏輯 */ } else { /** * 業(yè)務(wù)邏輯 */ } }}
優(yōu)缺點
優(yōu)點
構(gòu)造容易,邏輯簡單。
缺點
1.簡單工廠模式中的if else判斷非常多,完全是Hard Code,如果有一個新產(chǎn)品要加進來,就要同時添加一個新產(chǎn)品類,并且必須修改工廠類,再加入一個 else if 分支才可以, 這樣就
違背了 “開放-關(guān)閉原則“中的對修改關(guān)閉的準(zhǔn)則了。
2.一個工廠類中集合了所有的類的實例創(chuàng)建邏輯,
違反了高內(nèi)聚的責(zé)任分配原則,將全部的創(chuàng)建邏輯都集中到了一個工廠類當(dāng)中,所有的業(yè)務(wù)邏輯都在這個工廠類中實現(xiàn)。什么時候它不能工作了,整個系統(tǒng)都會受到影響。因此一般只在很簡單的情況下應(yīng)用,比如當(dāng)工廠類負(fù)責(zé)創(chuàng)建的對象比較少時。
3.簡單工廠模式由于使用了
靜態(tài)工廠方法,造成工廠角色無法形成基于繼承的等級結(jié)構(gòu)。適用環(huán)境
- 工廠類負(fù)責(zé)創(chuàng)建的對象比較少:由于創(chuàng)建的對象較少,不會造成工廠方法中的業(yè)務(wù)邏輯太過復(fù)雜。
JDK
①JDK類庫中廣泛使用了簡單工廠模式,如工具類java.text.DateFormat,它用于格式化一個本地日期或者時間。
public final static DateFormat getDateInstance();public final static DateFormat getDateInstance(int style);public final static DateFormat getDateInstance(int style,Localelocale);
②Java加密技術(shù)
獲取不同加密算法的密鑰生成器:
KeyGenerator keyGen=KeyGenerator.getInstance("DESede");
創(chuàng)建密碼器:
Cipher cp = Cipher.getInstance("DESede");
工廠方法(Factory Method)
https://www.jianshu.com/p/1cf9859e0f7c意圖
又稱為工廠模式/虛擬構(gòu)造器(Virtual Constructor)模式/
多態(tài)工廠(Polymorphic Factory)模式即通過工廠子類來確定究竟應(yīng)該實例化哪一個具體產(chǎn)品類。使用動機
不再設(shè)計一個按鈕工廠類來統(tǒng)一負(fù)責(zé)所有產(chǎn)品的創(chuàng)建,而是將具體按鈕的創(chuàng)建過程交給專門的工廠子類去完成。我們先定義一個抽象的按鈕工廠類,再定義具體的工廠類來生成圓形按鈕、矩形按鈕、菱形按鈕等,它們實現(xiàn)在抽象按鈕工廠類中定義的方法。這種抽象化的結(jié)果使這種結(jié)構(gòu)可以在不修改具體工廠類的情況下引進新的產(chǎn)品,如果出現(xiàn)新的按鈕類型,只需要為這種新類型的按鈕創(chuàng)建一個具體的工廠類就可以獲得該新按鈕的實例,這一特點無疑使得工廠方法模式具有超越簡單工廠模式的優(yōu)越性,更加符合“開閉原則”。
角色
- Product:抽象產(chǎn)品,工廠方法模式所創(chuàng)建的對象的超類,也就是所有產(chǎn)品類的共同父類或共同擁有的接口。在實際的系統(tǒng)中,這個角色也常常使用抽象類實現(xiàn)。
- ConcreteProduct:具體產(chǎn)品,這個角色實現(xiàn)了抽象產(chǎn)品(Product)所聲明的接口,工廠方法模式所創(chuàng)建的每一個對象都是某個具體產(chǎn)品的實例。
- Factory:抽象工廠,擔(dān)任這個角色的是工廠方法模式的核心,任何在模式中創(chuàng)建對象的工廠類必須實現(xiàn)這個接口。在實際的系統(tǒng)中,這個角色也常常使用抽象類實現(xiàn)。
- ConcreteFactory:具體工廠,擔(dān)任這個角色的是實現(xiàn)了抽象工廠接口的具體Java類。具體工廠角色含有與業(yè)務(wù)密切相關(guān)的邏輯,并且受到使用者的調(diào)用以創(chuàng)建具體產(chǎn)品對象。
實現(xiàn)
看鏈接內(nèi)
客戶端調(diào)用static void Main(string[] args) { //先給我來個燈泡 ICreator creator = new BulbCreator(); ILight light = creator.CreateLight(); light.TurnOn(); light.TurnOff(); //再來個燈管看看 creator = new TubeCreator(); light = creator.CreateLight(); light.TurnOn(); light.TurnOff(); }
優(yōu)缺點
優(yōu)點
①在工廠方法模式中,工廠方法用來創(chuàng)建客戶所需要的產(chǎn)品,
同時還向客戶隱藏了哪種具體產(chǎn)品類將被實例化這一細(xì)節(jié),用戶只需要關(guān)心所需產(chǎn)品對應(yīng)的工廠,無須關(guān)心創(chuàng)建細(xì)節(jié),甚至無須知道具體產(chǎn)品類的類名。
②
工廠方法模式之所以又被稱為多態(tài)工廠模式,是因為所有的具體工廠類都具有同一抽象父類。
③使用工廠方法模式的另一個優(yōu)點是在系統(tǒng)中加入新產(chǎn)品時,無須修改抽象工廠和抽象產(chǎn)品提供的接口,無須修改客戶端,也無須修改其他的具體工廠和具體產(chǎn)品,
而只要添加一個具體工廠和具體產(chǎn)品就可以了。這樣,系統(tǒng)的可擴展性也就變得非常好,完全符合“開閉原則”,這點比簡單工廠模式更優(yōu)秀。
缺點
①在添加新產(chǎn)品時,需要編寫新的具體產(chǎn)品類,而且還要提供與之對應(yīng)的具體工廠類,系統(tǒng)中類的個數(shù)將成對增加,在一定程度上增加了系統(tǒng)的復(fù)雜度,有更多的類需要編譯和運行,會給系統(tǒng)帶來一些額外的開銷。
②由于考慮到系統(tǒng)的可擴展性,需要引入抽象層,在客戶端代碼中均使用抽象層進行定義,增加了系統(tǒng)的抽象性和理解難度,且在實現(xiàn)時可能需要用到DOM、反射等技術(shù),增加了系統(tǒng)的實現(xiàn)難度。
JDK
Connection conn=DriverManager.getConnection("jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=DB;user=sa;password=");Statement statement=conn.createStatement();ResultSet rs=statement.executeQuery("select * from UserInfo");
- java.util.Calendar
- java.util.ResourceBundle
- java.text.NumberFormat
- java.nio.charset.Charset
- java.net.URLStreamHandlerFactory
- java.util.EnumSet
- javax.xml.bind.JAXBContext
抽象工廠(Abstract Factory)
https://www.jianshu.com/p/d6622f3e71ed定義
產(chǎn)品等級結(jié)構(gòu) :產(chǎn)品等級結(jié)構(gòu)即產(chǎn)品的繼承結(jié)構(gòu),如一個抽象類是電視機,其子類有海爾電視機、海信電視機、TCL電視機,則抽象電視機與具體品牌的電視機之間構(gòu)成了一個產(chǎn)品等級結(jié)構(gòu),抽象電視機是父類,而具體品牌的電視機是其子類。
產(chǎn)品族 :在抽象工廠模式中,產(chǎn)品族是指由同一個工廠生產(chǎn)的,位于不同產(chǎn)品等級結(jié)構(gòu)中的一組產(chǎn)品,如海爾電器工廠生產(chǎn)的海爾電視機、海爾電冰箱,海爾電視機位于電視機產(chǎn)品等級結(jié)構(gòu)中,海爾電冰箱位于電冰箱產(chǎn)品等級結(jié)構(gòu)中。
抽象工廠模式是所有形式的工廠模式中最為抽象和最具一般性的一種形態(tài)。抽象工廠模式與工廠方法模式最大的區(qū)別
工廠方法模式針對的是一個產(chǎn)品等級結(jié)構(gòu),而抽象工廠模式則需要面對多個產(chǎn)品等級結(jié)構(gòu),一個工廠等級結(jié)構(gòu)可以負(fù)責(zé)多個不同產(chǎn)品等級結(jié)構(gòu)中的產(chǎn)品對象的創(chuàng)建 。角色
抽象工廠模式包含如下角色:
- AbstractFactory:抽象工廠
- ConcreteFactory:具體工廠
- AbstractProduct:抽象產(chǎn)品
- Product:具體產(chǎn)品
實現(xiàn)
抽象產(chǎn)品: 蘋果系列
public interface Apple { void AppleStyle(); }
具體產(chǎn)品:iphone
public class iphone implements Apple { public void AppleStyle() { Console.WriteLine("Apple's style: iPhone!"); } }
抽象工廠
public interface Factory { Apple createAppleProduct(); Sumsung createSumsungProduct(); }
手機工廠
public class Factory_Phone implements Factory { public Apple createAppleProduct() { return new iphone(); } public Sumsung createSumsungProduct() { return new note2(); } }
調(diào)用
public static void Main(string[] args) { //采購商要一臺iPad和一臺Tab Factory factory = new Factory_Pad(); Apple apple = factory.createAppleProduct(); apple.AppleStyle(); Sumsung sumsung = factory.createSumsungProduct(); sumsung.BangziStyle(); //采購商又要一臺iPhone和一臺Note2 factory = new Factory_Phone(); apple = factory.createAppleProduct(); apple.AppleStyle(); sumsung = factory.createSumsungProduct(); sumsung.BangziStyle(); }
優(yōu)缺點
優(yōu)點
①應(yīng)用抽象工廠模式可以實現(xiàn)高內(nèi)聚低耦合的設(shè)計目的,因此抽象工廠模式得到了廣泛的應(yīng)用。
②
增加新的具體工廠和產(chǎn)品族很方便,因為一個具體的工廠實現(xiàn)代表的是一個產(chǎn)品族,無須修改已有系統(tǒng),符合“開閉原則”。
缺點
開閉原則的傾斜性(增加新的工廠和產(chǎn)品族容易,
增加新的產(chǎn)品等級結(jié)構(gòu)麻煩)
適用環(huán)境
在以下情況下可以使用抽象工廠模式:
①一個系統(tǒng)不應(yīng)當(dāng)依賴于產(chǎn)品類實例如何被創(chuàng)建、組合和表達(dá)的細(xì)節(jié),這對于所有類型的工廠模式都是重要的。
②系統(tǒng)中有多于一個的產(chǎn)品族,而每次只使用其中某一產(chǎn)品族。(
與工廠方法模式的區(qū)別)
③屬于同一個產(chǎn)品族的產(chǎn)品將在一起使用,這一約束必須在系統(tǒng)的設(shè)計中體現(xiàn)出來。
④系統(tǒng)提供一個產(chǎn)品類的庫,所有的產(chǎn)品以同樣的接口出現(xiàn),從而使客戶端不依賴于具體實現(xiàn)。
JDK
- javax.xml.parsers.DocumentBuilderFactory
- javax.xml.transform.TransformerFactory
- javax.xml.xpath.XPathFactory
生成器(Builder)
https://blog.csdn.net/c275046758/article/details/50540789意圖
封裝一個對象的構(gòu)造過程,并允許按步驟構(gòu)造。實現(xiàn)
image.png
JDK
- java.lang.StringBuilder
- java.nio.ByteBuffer
- java.lang.StringBuffer
- java.lang.Appendable
- Apache Camel builders
原型模式(Prototype)
https://www.cnblogs.com/lwbqqyumidi/p/3746821.html意圖
通過一個已經(jīng)存在的對象,復(fù)制出更多的具有與此對象具有相同類型的新的對象。
淺復(fù)制 clone():
我們知道,一個類的定義中包括屬性和方法。屬性用于表示對象的狀態(tài),方法用于表示對象所具有的行為。其中,屬性既可以是Java中基本數(shù)據(jù)類型,也可以是引用類型。
Java中的淺復(fù)制通常使用
clone()方式完成。
當(dāng)進淺復(fù)制時,clone函數(shù)返回的是一個引用,指向的是新的clone出來的對象,此對象與原對象分別占用不同的堆空間。同時,復(fù)制出來的對象具有與原對象一致的狀態(tài)。
此處對象一致的狀態(tài)是指:復(fù)制出的對象與原對象中的屬性值完全相等==。
深復(fù)制 deepclone():
Java中的深復(fù)制一般是通過對象的序列化和反序列化得以實現(xiàn)。序列化時,需要實現(xiàn)Serializable接口。
從輸出結(jié)果中可以看出,深復(fù)制不僅在堆內(nèi)存上開辟了空間以存儲復(fù)制出的對象,
甚至連對象中的引用類型的屬性所指向的對象也得以復(fù)制,重新開辟了堆空間存儲。JDK
- java.lang.Object#clone()
- deepclone()
行為型
責(zé)任鏈(Chain Of Responsibility)
https://blog.csdn.net/maoyuanming0806/article/details/80183494意圖
使多個對象都有機會處理請求,從而避免請求的發(fā)送者和接收者之間的耦合關(guān)系。將這些對象連成一條鏈,并沿著這條鏈發(fā)送該請求,直到有一個對象處理它為止。
在這種模式中,通常每個接收者都包含對另一個接收者的引用。如果一個對象不能處理該請求,那么它會把相同的請求傳給下一個接收者,依此類推。應(yīng)用實例
紅樓夢中的"擊鼓傳花"。 JS 中的事件冒泡。 JAVA WEB 中 Apache Tomcat 對 Encoding 的處理,Struts2 的攔截器,jsp servlet 的 Filter。
何時使用:在處理消息的時候以過濾很多道。
如何解決:
攔截的類都實現(xiàn)統(tǒng)一接口。關(guān)鍵代碼:Handler 里面聚合它自己,在 HandlerRequest 里判斷是否合適,如果沒達(dá)到條件則向下傳遞,向誰傳遞之前 set 進去。
JDK
- java.util.logging.Logger#log()
- Apache Commons Chain
- javax.servlet.Filter#doFilter()
命令(Command)
https://www.cnblogs.com/konck/p/4199907.html意圖
命令模式是為了解決命令的請求者和命令的實現(xiàn)者之間的耦合關(guān)系。
解決了這種耦合的好處我認(rèn)為主要有兩點:
1.更方便的對命令進行擴展(注意:這不是主要的優(yōu)勢,后面會提到)
2.對多個命令的統(tǒng)一控制(這種控制包括但不限于:隊列、撤銷/恢復(fù)、記錄日志等等)
應(yīng)用實例
struts 1 中的 action 核心控制器 ActionServlet 只有一個,相當(dāng)于 Invoker,而模型層的類會隨著不同的應(yīng)用有不同的模型類,相當(dāng)于具體的 Command。
類圖
- Command:命令
- Receiver:命令接收者,也就是命令真正的執(zhí)行者
- Invoker:通過它來調(diào)用命令
- Client:可以設(shè)置命令與命令的接收者
JDK
- java.lang.Runnable
- Netflix Hystrix
- javax.swing.Action
解釋器(Interpreter)
https://www.cnblogs.com/chenssy/p/3346427.html意圖
所謂解釋器模式就是定義語言的文法,并且建立一個解釋器來解釋該語言中的句子。這種模式實現(xiàn)了一個表達(dá)式接口,該接口解釋一個特定的上下文。這種模式被用在 SQL 解析、符號處理引擎等。
應(yīng)用實例
編譯器、運算表達(dá)式計算。
JDK
- java.util.Pattern
- java.text.Normalizer
- All subclasses of java.text.Format
- javax.el.ELResolver
迭代器(Iterator)
https://www.jianshu.com/p/3d0406a01b73意圖
提供一種順序訪問聚合對象元素的方法,并且不暴露聚合對象的內(nèi)部表示。
JDK
- java.util.Iterator
- java.util.Enumeration
中介者(Mediator)
http://www.runoob.com/design-pattern/mediator-pattern.html意圖
集中相關(guān)對象之間復(fù)雜的溝通和控制方式。
實現(xiàn)
Alarm(鬧鐘)、CoffeePot(咖啡壺)、Calendar(日歷)、Sprinkler(噴頭)是一組相關(guān)的對象,在某個對象的事件產(chǎn)生時需要去操作其它對象,形成了下面這種依賴結(jié)構(gòu):
使用中介者模式可以將復(fù)雜的依賴結(jié)構(gòu)變成星形結(jié)構(gòu):
[圖片上傳失敗...(image-93800a-1542466746560)]
[圖片上傳失敗...(image-38baf0-1542466746560)]
JDK
- All scheduleXXX() methods of java.util.Timer
- java.util.concurrent.Executor#execute()
- submit() and invokeXXX() methods of java.util.concurrent.ExecutorService
- scheduleXXX() methods of java.util.concurrent.ScheduledExecutorService
- java.lang.reflect.Method#invoke()
備忘錄(Memento)
https://blog.csdn.net/o279642707/article/details/60767258意圖
在不違反封裝的情況下獲得對象的內(nèi)部狀態(tài),從而在需要時可以將對象恢復(fù)到最初狀態(tài)。主要解決
所謂備忘錄模式就是在不破壞封裝的前提下,捕獲一個對象的內(nèi)部狀態(tài),并在該對象之外保存這個狀態(tài),這樣可以在以后將對象恢復(fù)到原先保存的狀態(tài)。
應(yīng)用實例
1、后悔藥。 2、打游戲時的存檔。 3、Windows 里的 ctri + z。 4、IE 中的后退。 4、數(shù)據(jù)庫的事務(wù)管理。
JDK
觀察者(Observer)
https://www.jianshu.com/p/fc4554cda68d意圖
觀察者模式是對象的行為模式,又叫發(fā)布-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監(jiān)聽器(Source/Listener)模式或從屬者(Dependents)模式。
觀察者模式定義了一種一對多的依賴關(guān)系,讓多個觀察者對象同時監(jiān)聽某一個主題對象。這個主題對象在狀態(tài)上發(fā)生變化時,會通知所有觀察者對象,使它們能夠自動更新自己。
[圖片上傳失敗...(image-f45dc1-1542466746560)]
何時使用
一個對象(目標(biāo)對象)的狀態(tài)發(fā)生改變,所有的依賴對象(觀察者對象)都將得到通知,進行廣播通知。
JDK
- java.util.Observer
- java.util.EventListener
- javax.servlet.http.HttpSessionBindingListener
- RxJava
狀態(tài)(State)
https://www.cnblogs.com/java-my-life/archive/2012/06/08/2538146.html意圖
允許對象在內(nèi)部狀態(tài)改變時改變它的行為,對象看起來好像修改了它所屬的類。應(yīng)用實例
考慮一個在線投票系統(tǒng)的應(yīng)用,要實現(xiàn)控制同一個用戶只能投一票,如果一個用戶反復(fù)投票,而且投票次數(shù)超過5次,則判定為惡意刷票,要取消該用戶投票的資格,當(dāng)然同時也要取消他所投的票;如果一個用戶的投票次數(shù)超過8次,將進入黑名單,禁止再登錄和使用系統(tǒng)。
策略(Strategy)
https://www.jianshu.com/p/7fa8ad000a97https://www.cnblogs.com/wenjiang/p/3352041.html意圖
定義一系列算法,封裝每個算法,并使它們可以互換。策略模式和狀態(tài)模式的區(qū)別:之所以說狀態(tài)模式是策略模式的孿生兄弟,是因為它們的UML圖是一樣的,但意圖卻完全不一樣,策略模式是讓用戶指定更換的策略算法,而狀態(tài)模式是狀態(tài)在滿足一定條件下的自動更換,用戶無法指定狀態(tài),最多只能設(shè)置初始狀態(tài)。
策略模式可以讓算法獨立于使用它的客戶端。
具體場景實現(xiàn)
假設(shè)現(xiàn)在要設(shè)計一個販賣各類書籍的電子商務(wù)網(wǎng)站的購物車系統(tǒng)。一個最簡單的情況就是把所有貨品的單價乘上數(shù)量,但是實際情況肯定比這要復(fù)雜。比如,本網(wǎng)站可能對所有的高級會員提供每本20%的促銷折扣;對中級會員提供每本10%的促銷折扣;對初級會員沒有折扣。
根據(jù)描述,折扣是根據(jù)以下的幾個算法中的一個進行的:
算法一:對初級會員沒有折扣。
算法二:對中級會員提供10%的促銷折扣。
算法三:對高級會員提供20%的促銷折扣。
public static void main(String[] args) { //選擇并創(chuàng)建需要使用的策略對象 MemberStrategy strategy = new AdvancedMemberStrategy(); //創(chuàng)建環(huán)境 Price price = new Price(strategy); //計算價格 double quote = price.quote(300); System.out.println("圖書的最終價格為:" + quote); }
策略模式對多態(tài)的使用
通過讓環(huán)境類持有一個抽象策略類(超類)的引用,在生成環(huán)境類實例對象時,
讓該引用指向具體的策略子類。再對應(yīng)的方法調(diào)用中,就會通過Java的多態(tài),調(diào)用對應(yīng)策略子類的方法。從而可以相互替換,不需要修改環(huán)境類內(nèi)部的實現(xiàn)。同時,在有新的需求的情況下,也只需要修改策略類即可,降低與環(huán)境類之間的耦合度。
策略模式和工廠方法的異同
工廠模式和策略模式的區(qū)別在于實例化一個對象的位置不同,對工廠模式而言,實例化對象是放在服務(wù)端的,即放在了工廠類里面;而策略模式實例化對象的操作在客戶端工廠模式要求服務(wù)端的銷售部門足夠靈敏,而策略模式由于對策略進行了封裝,所以他的銷售部門比較傻,需要客戶提供足夠能區(qū)分使用哪種策略的參數(shù),而這最好的就是該策略的實例了。
JDK
- java.util.Comparator#compare()
- javax.servlet.http.HttpServlet
- javax.servlet.Filter#doFilter()
模板方法(Template Method)
https://www.jianshu.com/p/cc391b56bd0e典型用例:Spring定義
模板方法模式是類的行為模式。準(zhǔn)備一個抽象類,將部分邏輯以具體方法以及具體構(gòu)造函數(shù)的形式實現(xiàn),然后聲明一些抽象方法來迫使子類實現(xiàn)剩余的邏輯。不同的子類可以以不同的方式實現(xiàn)這些抽象方法,從而對剩余的邏輯有不同的實現(xiàn)。這就是模板方法模式的用意。
結(jié)構(gòu)
模板方法中的方法可以分為兩大類:模板方法和基本方法。
模板方法
一個模板方法是定義在抽象類中的,把基本操作方法組合在一起形成一個總算法或一個總行為的方法。
一個抽象類可以有任意多個模板方法,而不限于一個。每一個模板方法都可以調(diào)用任意多個具體方法。
基本方法
基本方法又可以分為三種
- 抽象方法:一個抽象方法由抽象類聲明,由具體子類實現(xiàn)。在Java語言里抽象方法以abstract關(guān)鍵字標(biāo)示。
- 具體方法:一個具體方法由抽象類聲明并實現(xiàn),而子類并不實現(xiàn)或置換。
- 鉤子方法:一個鉤子方法由抽象類聲明并實現(xiàn),而子類會加以擴展。通常抽象類給出的實現(xiàn)是一個空實現(xiàn),作為方法的默認(rèn)實現(xiàn)。
默認(rèn)鉤子方法一個鉤子方法常常由抽象類給出一個空實現(xiàn)作為此方法的默認(rèn)實現(xiàn)。這種空的鉤子方法叫做“Do Nothing Hook”。具體模版類中可以選擇是否重寫鉤子方法,
通常重寫鉤子方法是為了對模版方法中的步驟進行控制,判斷鉤子方法中的狀態(tài),是否進行下一步操作。使用場景
模板方法模式是基于繼承的代碼復(fù)用技術(shù),它體現(xiàn)了面向?qū)ο蟮闹T多重要思想,是一種使用較為頻繁的模式。模板方法模式廣泛應(yīng)用于框架設(shè)計中,以確保通過父類來控制處理流程的邏輯順序(如框架的初始化,測試流程的設(shè)置等)。
JDK
- java.util.Collections#sort()
- java.io.InputStream#skip()
- java.io.InputStream#read()
- java.util.AbstractList#indexOf()
訪問者(Visitor)
https://www.jianshu.com/p/80b9cd7c0da5什么是訪問者模式?
比如我有一個賬單,賬單有收入,支出兩個固定方法。但是訪問賬單的人不確定,有可能是一個或者多個。
訪問者模式有兩個特點
一般被訪問的東西所持有的方法是固定的,就像賬單只有收入和支出兩個功能。而訪問者是不固定的。
數(shù)據(jù)操作與數(shù)據(jù)結(jié)構(gòu)相分離:頻繁的更改數(shù)據(jù),但不結(jié)構(gòu)不變。比如:雖然每一天賬單的數(shù)據(jù)都會變化(數(shù)據(jù)變化),但是只有兩類數(shù)據(jù),就是支出和收入(結(jié)構(gòu)不變)。
代碼
見參考網(wǎng)頁
結(jié)構(gòu)型設(shè)計模式請看下篇參考
簡書大牛:
https://www.jianshu.com/nb/10186551Github:
https://github.com/CyC2018/Interview-Notebook/blob/master/notes/設(shè)計模式.md
菜鳥網(wǎng):
http://www.runoob.com/design-pattern/design-pattern-tutorial.html補充:23種設(shè)計模式總結(jié):
https://www.cnblogs.com/tongkey/p/7170826.htmlhttps://www.cnblogs.com/malihe/p/6891920.html
關(guān)鍵詞:模式,創(chuàng)建,設(shè)計,梳理