【硬核】23種設計模式娓娓道來,助你優雅的編寫出漂亮程式碼!
大家好,我是小羽。
我們平時使用的每一個技術棧的原理或者源碼都或多或少與設計模式的理念有關聯,也可以這麼說,只有更好的掌握了設計模式,我們的程式碼編寫才能更規範、簡潔,效率更高。
其次,設計模式大多都是經過我們的前輩的經驗反覆總結而成,站在巨人的肩膀上,吸收他們的經驗教訓,我們的編碼之路才會走的更長久。
同時,在我們的面試過程中也是加分的選項,你如果將設計模式能跟面試官娓娓道來,面試官肯定會對你刮目相看的。工作中,擁有良好的設計模式思想,對於項目的開發也會有很大的幫助。
接下來,跟著小羽一起來看看我們需要了解的設計模式都有哪些呢~
前言
總體來說設計模式分為三大類:
_創建型模式:_工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。
_結構型模式:_適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。
_行為型模式:_策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、解釋器模式。
單例模式
概念
確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例。
使用場景
-
要求生成唯一序列號的環境;
-
在整個項目中需要一個共享訪問點或共享數據,例如一個Web頁面上的計數器,可以不用把每次刷新都記錄到資料庫中,使用單例模式保持計數器的值,並確保是執行緒安全的;
-
創建一個對象需要消耗的資源過多,如要訪問IO和資料庫等資源;
-
需要定義大量的靜態常量和靜態方法(如工具類)的環境,可以採用單例模式(當然,也可以直接聲明為static的方式)。
程式碼示例
執行緒安全:
`public class Singleton {`
`private static final Singleton singleton = new Singleton();`
`//限制產生多個對象`
`private Singleton(){`
`}`
`//通過該方法獲得實例對象`
`public static Singleton getSingleton(){`
`return singleton;`
`}`
`//類中其他方法,盡量是 static`
`public static void doSomething(){`
`}`
`}`
執行緒不安全:
`public class Singleton {`
`private static Singleton singleton = null;`
`//限制產生多個對象`
`private Singleton(){`
`}`
`//通過該方法獲得實例對象`
`public static Singleton getSingleton(){`
`if(singleton == null){`
`singleton = new Singleton();`
`}`
`return singleton;`
`}`
`}`
針對執行緒不安全:
在 getSingleton 方法前加 synchronized 關鍵字,也可以在 getSingleton 方法內增加synchronized 來實現。
工廠模式
概念
定義一個用於創建對象的介面,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類。
使用場景
jdbc 連接資料庫,硬體訪問,降低對象的產生和銷毀
結構
_簡單工廠模式:_一個模組僅需要一個工廠類,沒有必要把它產生出來,使用靜態的方法
_多個工廠類:_每個人種(具體的產品類)都對應了一個創建者,每個創建者獨立負責創建對應的產品對象,非常符合單一職責原則
_代替單例模式:_單例模式的核心要求就是在記憶體中只有一個對象,通過工廠方法模式也可以只在記憶體中生產一個對象
_延遲初始化:_ProductFactory 負責產品類對象的創建工作,並且通過 prMap 變數產生一個快取,對需要再次被重用的對象保留
程式碼示例
Product 為抽象產品類負責定義產品的共性,實現對事物最抽象的定義;
Creator 為抽象創建類,也就是抽象工廠,具體如何創建產品類是由具體的實現工廠 ConcreteCreator 完成的。
`public class ConcreteCreator extends Creator {`
`public <T extends Product> T createProduct(Class<T> c){`
`Product product=null;`
`try {`
`product =`
`(Product)Class.forName(c.getName()).newInstance();`
`} catch (Exception e) {`
`//異常處理`
`}`
`return (T)product;`
`}`
`}`
抽象工廠模式
概念
為創建一組相關或相互依賴的對象提供一個介面,而且無須指定它們的具體類。
使用場景
一個對象族(或是一組沒有任何關係的對象)都有相同的約束。
涉及不同作業系統的時候,都可以考慮使用抽象工廠模式。
程式碼示例
`public abstract class AbstractCreator {`
`//創建 A 產品家族`
`public abstract AbstractProductA createProductA();`
`//創建 B 產品家族`
`public abstract AbstractProductB createProductB();`
`}`
模板方法模式
概念
定義一個操作中的演算法的框架,而將一些步驟延遲到子類中。使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟。
使用場景
-
多個子類有公有的方法,並且邏輯基本相同時。
-
重要、複雜的演算法,可以把核心演算法設計為模板方法,周邊的相關細節功能則由各個子類實現。
-
重構時,模板方法模式是一個經常使用的模式,把相同的程式碼抽取到父類中,然後通過鉤子函數(見「模板方法模式的擴展」)約束其行為。
結構
抽象模板:AbstractClass
為抽象模板,它的方法分為兩類:
1、基本方法:也叫做基本操作,是由子類實現的方法,並且在模板方法被調用。
2、模板方法:可以有一個或幾個,一般是一個具體方法,也就是一個框架,實現對基本方法的調度,完成固定的邏輯。
注意: 為了防止惡意的操作,一般模板方法都加上 final
關鍵字,不允許被覆寫。
_具體模板:_實現父類所定義的一個或多個抽象方法,也就是父類定義的基本方法在子類中得以實現。
程式碼示例
`package templateMethod;`
`public class TemplateMethodPattern`
`{`
`public static void main(String[] args)`
`{`
`AbstractClass tm=new ConcreteClass();`
`tm.TemplateMethod();`
`}`
`}`
`//抽象類`
`abstract class AbstractClass`
`{`
`public void TemplateMethod() //模板方法`
`{`
`SpecificMethod();`
`abstractMethod1();`
`abstractMethod2();`
`}`
`public void SpecificMethod() //具體方法`
`{`
`System.out.println("抽象類中的具體方法被調用...");`
`}`
`public abstract void abstractMethod1(); //抽象方法1`
`public abstract void abstractMethod2(); //抽象方法2`
`}`
`//具體子類`
`class ConcreteClass extends AbstractClass`
`{`
`public void abstractMethod1()`
`{`
`System.out.println("抽象方法1的實現被調用...");`
`}`
`public void abstractMethod2()`
`{`
`System.out.println("抽象方法2的實現被調用...");`
`}`
`}`
建造者模式
概念
將一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。
使用場景
-
相同的方法,不同的執行順序,產生不同的事件結果時,可以採用建造者模式。
-
多個部件或零件,都可以裝配到一個對象中,但是產生的運行結果又不相同時,則可以使用該模式。
-
產品類非常複雜,或者產品類中的調用順序不同產生了不同的效能,這個時候使用建造者模式非常合適。
結構
_Product 產品類:_通常是實現了模板方法模式,也就是有模板方法和基本方法。
_Builder 抽象建造者:_規範產品的組建,一般是由子類實現。
_ConcreteBuilder 具體建造者:_實現抽象類定義的所有方法,並且返回一個組建好的對象。
_Director 導演類:_負責安排已有模組的順序,然後告訴 Builder 開始建造
程式碼示例
`public class ConcreteProduct extends Builder {`
`private Product product = new Product();`
`//設置產品零件`
`public void setPart(){`
`/*`
`* 產品類內的邏輯處理`
`*/`
`}`
`//組建一個產品`
`public Product buildProduct() {`
`return product;`
`}`
`}`
代理模式
概念
為其他對象提供一種代理以控制對這個對象的訪問。
結構
_Subject 抽象主題角色:_抽象主題類可以是抽象類也可以是介面,是一個最普通的業務類型定義,無特殊要求。
_RealSubject 具體主題角色:_也叫做被委託角色、被代理角色。它才是冤大頭,是業務邏輯的具體執行者。
_Proxy 代理主題角色:_也叫做委託類、代理類。它負責對真實角色的應用,把所有抽象主題類定義的方法、限制委託給真實主題角色實現,並且在真實主題角色處理完畢前後做預處理和善後處理工作。
分類
_普通代理:_在該模式下,調用者只知代理而不用知道真實的角色是誰,屏蔽了真實角色的變更對高層模組的影響,真實的主題角色想怎麼修改就怎麼修改,對高層次的模組沒有任何的影響,只要你實現了介面所對應的方法,該模式非常適合對擴展性要求較高的場合。
_強制代理:_強制代理的概念就是要從真實角色查找到代理角色,不允許直接訪問真實角色。高層模組只要調用 getProxy 就可以訪問真實角色的所有方法,它根本就不需要產生一個代理出來,代理的管理已經由真實角色自己完成。
- 區別:普通代理就是我們要知道代理的存在,然後才能訪問;強制代理則是調用者直接調用真實角色,而不用關心代理是否存在,其代理的產生是由真實角色決定的。
_動態代理:_根據被代理的介面生成所有的方法,也就是說給定一個介面,動態代理會宣稱「我已經實現該介面下的所有方法了」。兩條獨立發展的線路。動態代理實現代理的職責,業務邏輯實現相關的邏輯功能,兩者之間沒有必然的相互耦合的關係。通知從另一個切面切入,最終在高層模組進行耦合,完成邏輯的封裝任務。
-
意圖:橫切面編程,在不改變我們已有程式碼結構的情況下增強或控制對象的行為。
-
首要條件:被代理的類必須要實現一個介面。
程式碼示例
`public Object getProxy(@Nullable ClassLoader classLoader) {`
`if (logger.isTraceEnabled()) {`
`logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());`
`}`
`Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);`
`findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);`
`return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);`
`}`
原型模式
概念
用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。
使用場景
_資源優化場景:_類初始化需要消化非常多的資源,這個資源包括數據、硬體資源等。
_性能和安全要求的場景:_通過 new 產生一個對象需要非常繁瑣的數據準備或訪問許可權,則可以使用原型模式。
_一個對象多個修改者的場景:_一個對象需要提供給其他對象訪問,而且各個調用者可能都需要修改其值時,可以、考慮使用原型模式拷貝多個對象供調用者使用。
優點
原型模式實際上就是實現 Cloneable 介面,重寫 clone()方法。
_性能優良:_原型模式是在記憶體二進位流的拷貝,要比直接 new 一個對象性能好很多,特別是要在一個循環體內產生大量的對象時,原型模式可以更好地體現其優點。
_逃避構造函數的約束:_這既是它的優點也是缺點,直接在記憶體中拷貝,構造函數是不會執行的。
程式碼示例
`public class PrototypeClass implements Cloneable{`
`//覆寫父類 Object 方法`
`@Override`
`public PrototypeClass clone(){`
`PrototypeClass prototypeClass = null;`
`try {`
`prototypeClass = (PrototypeClass)super.clone();`
`} catch (CloneNotSupportedException e) {`
`//異常處理`
`}`
`return prototypeClass;`
`}`
`}`
中介者模式
概念
用一個中介對象封裝一系列的對象交互,中介者使各對象不需要顯示地相互作用,從而使其耦合鬆散,而且可以獨立地改變它們之間的交互。
使用場景
中介者模式適用於多個對象之間緊密耦合的情況,緊密耦合的標準是:在類圖中出現了蜘蛛網狀結構,即每個類都與其他的類有直接的聯繫。
結構
_Mediator 抽象中介者角色:_抽象中介者角色定義統一的介面,用於各同事角色之間的通訊。
_Concrete Mediator 具體中介者角色:_具體中介者角色通過協調各同事角色實現協作行為,因此它必須依賴於各個同事角色。
_Colleague 同事角色:_每一個同事角色都知道中介者角色,而且與其他的同事角色通訊的時候,一定要通過中介者角色協作。每個同事類的行為分為兩種:一種是同事本身的行為,比如改變對象本身的狀態,處理自己的行為等,這種行為叫做自發行為(SelfMethod),與其他的同事類或中介者沒有任何的依賴;第二種是必須依賴中介者才能完成的行為,叫做依賴方法(Dep-Method)。
示例程式碼
`public abstract class Mediator {`
`//定義同事類`
`protected ConcreteColleague1 c1;`
`protected ConcreteColleague2 c2;`
`//通過 getter/setter 方法把同事類注入進來`
`public ConcreteColleague1 getC1() {`
`return c1;`
`}`
`public void setC1(ConcreteColleague1 c1) {`
`this.c1 = c1;`
`}`
`public ConcreteColleague2 getC2() {`
`return c2;`
`}`
`public void setC2(ConcreteColleague2 c2) {`
`this.c2 = c2;`
`}`
`//中介者模式的業務邏輯`
`public abstract void doSomething1();`
`public abstract void doSomething2();`
`}`
命令模式
概念
將一個請求封裝成一個對象,從而讓你使用不同的請求把客戶端參數化,對請求排隊或者記錄請求日誌,可以提供命令的撤銷和恢復功能。
使用場景
認為是命令的地方就可以採用命令模式,例如,在 GUI 開發中,一個按鈕的點擊是一個命令,可以採用命令模式;模擬 DOS 命令的時候,當然也要採用命令模式;觸發-回饋機制的處理等。
結構
_Receive 接收者角色:_該角色就是幹活的角色,命令傳遞到這裡是應該被執行的,具體到我們上面的例子中就是 Group 的三個實現類(需求組,美工組,程式碼組)。
_Command 命令角色:_需要執行的所有命令都在這裡聲明。
_Invoker 調用者角色:_接收到命令,並執行命令。在例子中,我(項目經理)就是這個角色。
程式碼示例
`public class Invoker {`
`private Command command;`
`// 設值注入`
`public void setCommand(Command command) {`
`this.command = command;`
`}`
`// 執行命令`
`public void action() {`
`this.command.execute();`
`}`
`}`
責任鏈模式
概念
使多個對象都有機會處理請求,從而避免了請求的發送者和接受者之間的耦合關係。將這些對象連成一條鏈,並沿著這條鏈傳遞該請求,直到有對象處理它為止。
職責
抽象的處理者實現三個職責:
1、定義一個請求的處理方法 handleMessage,唯一對外開放的方法;
2、定義一個鏈的編排方法 setNext,設置下一個處理者;
3、定義了具體的請求者必須實現的兩個方法:定義自己能夠處理的級別getHandlerLevel 和具體的處理任務 echo。
程式碼示例
`public abstract class Handler {`
`private Handler nextHandler;`
`//每個處理者都必須對請求做出處理`
`public final Response handleMessage(Request request){`
`Response response = null;`
`//判斷是否是自己的處理級別`
`if(this.getHandlerLevel().equals(request.getRequestLevel())){`
`response = this.echo(request);`
`}else{ //不屬於自己的處理級別`
`//判斷是否有下一個處理者`
`if(this.nextHandler != null){`
`response =`
`this.nextHandler.handleMessage(request);`
`}else{`
`//沒有適當的處理者,業務自行處理`
`} }`
`return response;`
`}`
`//設置下一個處理者是誰`
`public void setNext(Handler _handler){`
`this.nextHandler = _handler;`
`}`
`//每個處理者都有一個處理級別`
`protected abstract Level getHandlerLevel();`
`//每個處理者都必須實現處理任務`
`protected abstract Response echo(Request request);`
`}`
注意事項
鏈中節點數量需要控制,避免出現超長鏈的情況,一般的做法是在 Handler 中設置一個最大節點數量,在 setNext 方法中判斷是否已經是超過其閾值,超過則不允許該鏈建立,避免無意識地破壞系統性能。
裝飾模式
概念
動態地給一個對象添加一些額外的職責。就增加功能來說,裝飾模式相比生成子類更為靈活。
使用場景
-
需要擴展一個類的功能,或給一個類增加附加功能。
-
需要動態地給一個對象增加功能,這些功能可以再動態地撤銷。
-
需要為一批的兄弟類進行改裝或加裝功能,當然是首選裝飾模式。
結構
_Component 抽象構件:_Component 是一個介面或者是抽象類,就是定義我們最核心的對象,也就是最原始的對象。在裝飾模式中,必然有一個最基本、最核心、最原始的介面或抽象類充當Component 抽象構件。
_ConcreteComponent 具體構件:_ConcreteComponent 是最核心、最原始、最基本的介面或抽象類的實現,你要裝飾的就是它。
_Decorator 裝飾角色:_一般是一個抽象類,做什麼用呢?實現介面或者抽象方法,它裡面可不一定有抽象的方法呀,在它的屬性里必然有一個 private 變數指向 Component 抽象構件。
_具體裝飾角色:_兩個具體的裝飾類,你要把你最核心的、最原始的、最基本的東西裝飾成其他東西。
程式碼示例
`/**`
`* 裝飾角色`
`*/`
`@Data`
`@AllArgsConstructor`
`@NoArgsConstructor`
`@Log`
`class BufferedReader implements Reader{`
`private Reader reader;`
`@Override`
`public void read() {`
`reader.read();`
`}`
`public void readLine(){`
`read();`
`log.info("並且僅僅讀取一行");`
`}`
`}`
策略模式
概念
定義一組演算法,將每個演算法都封裝起來,並且使它們之間可以互換。
使用場景
-
多個類只有在演算法或行為上稍有不同的場景。
-
演算法需要自由切換的場景。
-
需要屏蔽演算法規則的場景。
-
具體策略數量超過 4 個,則需要考慮使用混合模式
結構
_Context 封裝角色:_它也叫做上下文角色,起承上啟下封裝作用,屏蔽高層模組對策略、演算法的直接訪問,封裝可能存在的變化。
_Strategy 抽象策略角色:_策略、演算法家族的抽象,通常為介面,定義每個策略或演算法必須具有的方法和屬性。
_ConcreteStrategy 具體策略角色:_實現抽象策略中的操作,該類含有具體的演算法。
程式碼示例
`public enum Calculator {`
`//加法運算`
`ADD("+"){`
`public int exec(int a,int b){`
`return a+b;`
`}`
`},`
`//減法運算`
`SUB("-"){`
`public int exec(int a,int b){`
`return a - b;`
`}`
`};`
`String value = "";`
`//定義成員值類型`
`private Calculator(String _value){`
`this.value = _value;`
`}`
`//獲得枚舉成員的值`
`public String getValue(){`
`return this.value;`
`}`
`//聲明一個抽象函數`
`public abstract int exec(int a,int b);`
`}`
適配器模式
概念
將一個類的介面變換成客戶端所期待的另一種介面,從而使原本因介面不匹配而無法在一起工作的兩個類能夠在一起工作。
使用場景
你有動機修改一個已經投產中的介面時,適配器模式可能是最適合你的模式。比如系統擴展了,需要使用一個已有或新建立的類,但這個類又不符合系統的介面,怎麼辦?詳細設計階段不要考慮使用適配器模式,使用主要場景為擴展應用中。
類適配器
_Target 目標角色:_該角色定義把其他類轉換為何種介面,也就是我們的期望介面。
_Adaptee 源角色:_你想把誰轉換成目標角色,這個「誰」就是源角色,它是已經存在的、運行良好的類或對象,經過適配器角色的包裝,它會成為一個嶄新、靚麗的角色。
_Adapter 適配器角色:_適配器模式的核心角色,其他兩個角色都是已經存在的角色,而適配器角色是需要新建立的,它的職責非常簡單:把源角色轉換為目標角色,怎麼轉換?通過繼承或是類關聯的方式。
對象適配器
不使用多繼承或繼承的方式,而是使用直接關聯,或者稱為委託的方式。
對象適配器和類適配器的區別:
類適配器是類間繼承,對象適配器是對象的合成關係,也可以說是類的關聯關係,這是兩者的根本區別。實際項目中對象適配器使用到的場景相對比較多。
程式碼示例
`public class Adapter extends Target`
`{`
`private Adaptee adaptee;`
`public Adapter(Adaptee adaptee)`
`{`
`this.adaptee=adaptee;`
`}`
`public void request()`
`{`
`adaptee.specificRequest();`
`}`
`}`
迭代器模式
概念
它提供一種方法訪問一個容器對象中各個元素,而又不需暴露該對象的內部細節。
結構
_Iterator 抽象迭代器:_抽象迭代器負責定義訪問和遍曆元素的介面,而且基本上是有固定的 3 個方法:first()獲得第一個元素,next()訪問下一個元素,isDone()是否已經訪問到底部(Java 叫做 hasNext()方法)。
_ConcreteIterator 具體迭代器:_具體迭代器角色要實現迭代器介面,完成容器元素的遍歷。
_Aggregate 抽象容器:_容器角色負責提供創建具體迭代器角色的介面,必然提供一個類似createIterator()這樣的方法,在 Java 中一般是 iterator()方法。
_Concrete Aggregate 具體容器:_具體容器實現容器介面定義的方法,創建出容納迭代器的對象。
程式碼示例
`/**`
`* 具體迭代器`
`*/`
`public class ConcreteIterator<T> implements Iterator<T> {`
`private List<T> list = new ArrayList<>();`
`private int cursor = 0;`
`public boolean hasNext() {`
`return cursor != list.size();`
`}`
`public T next() {`
`T obj = null;`
`if (this.hasNext()) {`
`obj = this.list.get(cursor++);`
`}`
`return obj;`
`}`
`}`
組合模式
概念
將對象組合成樹形結構以表示「部分-整體」的層次結構,使得用戶對單個對象和組合對象的使用具有一致性。
使用場景
-
維護和展示部分-整體關係的場景,如樹形菜單、文件和文件夾管理。
-
從一個整體中能夠獨立出部分模組或功能的場景。
-
只要是樹形結構,就考慮使用組合模式。
結構
Component 抽象構件角色:定義參加組合對象的共有方法和屬性,可以定義一些默認的行為或屬性。
_Leaf 葉子構件:_葉子對象,其下再也沒有其他的分支,也就是遍歷的最小單位。
_Composite 樹枝構件:_樹枝對象,它的作用是組合樹枝節點和葉子節點形成一個樹形結構。
程式碼示例
`public class Composite extends Component {`
`//構件容器`
`private ArrayList<Component> componentArrayList = new`
`ArrayList<Component>();`
`//增加一個葉子構件或樹枝構件`
`public void add(Component component){`
`this.componentArrayList.add(component);`
`}`
`//刪除一個葉子構件或樹枝構件`
`public void remove(Component component){`
`this.componentArrayList.remove(component);`
`}`
`//獲得分支下的所有葉子構件和樹枝構件`
`public ArrayList<Component> getChildren(){`
`return this.componentArrayList;`
`}`
`}`
觀察者模式
概念
定義對象間一種一對多的依賴關係,使得每當一個對象改變狀態,則所有依賴於它的對象都會得到通知並被自動更新。
使用場景
-
關聯行為場景。需要注意的是,關聯行為是可拆分的,而不是「組合」關係。
-
事件多級觸發場景。
-
跨系統的消息交換場景,如消息隊列的處理機制。
結構
_Subject 被觀察者:_定義被觀察者必須實現的職責,它必須能夠動態地增加、取消觀察者。它一般是抽象類或者是實現類,僅僅完成作為被觀察者必須實現的職責:管理觀察者並通知觀察者。
_Observer 觀察者:_觀察者接收到消息後,即進行 update(更新方法)操作,對接收到的資訊進行處理。
_ConcreteSubject 具體的被觀察者:_定義被觀察者自己的業務邏輯,同時定義對哪些事件進行通知。
_ConcreteObserver 具體的觀察者:_每個觀察在接收到消息後的處理反應是不同,各個觀察者有自己的處理邏輯。
程式碼示例
`public abstract class Subject {`
`//定義一個觀察者數組`
`private Vector<Observer> obsVector = new Vector<Observer>();`
`//增加一個觀察者`
`public void addObserver(Observer o){`
`this.obsVector.add(o);`
`}`
`//刪除一個觀察者`
`public void delObserver(Observer o){`
`this.obsVector.remove(o);`
`}`
`//通知所有觀察者`
`public void notifyObservers(){`
`for(Observer o:this.obsVector){`
`o.update();`
`}`
`}`
`}`
門面模式
概念
要求一個子系統的外部與其內部的通訊必須通過一個統一的對象進行。門面模式提供一個高層次的介面,使得子系統更易於使用。
使用場景
-
為一個複雜的模組或子系統提供一個供外界訪問的介面
-
子系統相對獨立——外界對子系統的訪問只要黑箱操作即可
-
預防低水平人員帶來的風險擴散
結構
_Facade 門面角色:_客戶端可以調用這個角色的方法。此角色知曉子系統的所有功能和責任。一般情況下,本角色會將所有從客戶端發來的請求委派到相應的子系統去,也就說該角色沒有實際的業務邏輯,只是一個委託類。
_subsystem 子系統角色:_可以同時有一個或者多個子系統。每一個子系統都不是一個單獨的類,而是一個類的集合。子系統並不知道門面的存在。對於子系統而言,門面僅僅是另外一個客戶端而已。
程式碼模式
`public class Client {`
`//委託的子系統對象`
`private A a= new A();`
`private B b= new B();`
`private C c= new C();`
`//提供外部訪問的方法`
`public void methodA(){`
`this.a.doSomething();`
`}`
`public void methodB(){`
`this.b.doSomething();`
`}`
`public void methodC(){`
`this.c.doSomething();`
`}`
`}`
備忘錄模式
概念
在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態。這樣以後就可將該對象恢復到原先保存的狀態。
使用場景
-
需要保存和恢複數據的相關狀態場景。
-
提供一個可回滾(rollback)的操作。
-
需要監控的副本場景中。
-
資料庫連接的事務管理就是用的備忘錄模式。
結構
_Originator 發起人角色:_記錄當前時刻的內部狀態,負責定義哪些屬於備份範圍的狀態,負責創建和恢復備忘錄數據。
_Memento 備忘錄角色:_負責存儲 Originator 發起人對象的內部狀態,在需要的時候提供發起人需要的內部狀態。
_Caretaker 備忘錄管理員角色:_對備忘錄進行管理、保存和提供備忘錄。
程式碼示例
`public class BeanUtils {`
`//把 bean 的所有屬性及數值放入到 Hashmap 中`
`public static HashMap<String,Object> backupProp(Object bean){`
`HashMap<String,Object> result = new`
`HashMap<String,Object>();`
`try {`
`//獲得 Bean 描述`
`BeanInfo`
`beanInfo=Introspector.getBeanInfo(bean.getClass());`
`//獲得屬性描述`
`PropertyDescriptor[]`
`descriptors=beanInfo.getPropertyDescriptors();`
`//遍歷所有屬性`
`for(PropertyDescriptor des:descriptors){`
`//屬性名稱`
`String fieldName = des.getName();`
`//讀取屬性的方法`
`Method getter = des.getReadMethod();`
`//讀取屬性值`
`Object fieldValue=getter.invoke(bean,new`
`Object[]{});`
`if(!fieldName.equalsIgnoreCase("class")){`
`result.put(fieldName, fieldValue);`
`} } } catch (Exception e) {`
`//異常處理`
`}`
`return result;`
`}`
`//把 HashMap 的值返回到 bean 中`
`public static void restoreProp(Object bean,HashMap<String,Object>`
`propMap){`
`try {`
`//獲得 Bean 描述`
`BeanInfo beanInfo =`
`Introspector.getBeanInfo(bean.getClass());`
`//獲得屬性描述`
`PropertyDescriptor[] descriptors =`
`beanInfo.getPropertyDescriptors();`
`//遍歷所有屬性`
`for(PropertyDescriptor des:descriptors){`
`//屬性名稱`
`String fieldName = des.getName();`
`//如果有這個屬性`
`if(propMap.containsKey(fieldName)){`
`//寫屬性的方法`
`Method setter = des.getWriteMethod();`
`setter.invoke(bean, new`
`Object[]{propMap.get(fieldName)});`
`} } } catch (Exception e) {`
`//異常處理`
`System.out.println("shit");`
`e.printStackTrace();`
`}`
`}`
`}`
訪問者模式
概念
封裝一些作用於某種數據結構中的各元素的操作,它可以在不改變數據結構的前提下定義作用於這些元素的新的操作。
使用場景
-
一個對象結構包含很多類對象,它們有不同的介面,而你想對這些對象實施一些依賴於其具體類的操作,也就說是用迭代器模式已經不能勝任的情景。
-
需要對一個對象結構中的對象進行很多不同並且不相關的操作,而你想避免讓這些操作「污染」這些對象的類。
結構
_Visitor——抽象訪問者:_抽象類或者介面,聲明訪問者可以訪問哪些元素,具體到程式中就是 visit 方法的參數定義哪些對象是可以被訪問的。
_ConcreteVisitor——具體訪問者:_它影響訪問者訪問到一個類後該怎麼干,要做什麼事情。
_Element——抽象元素:_介面或者抽象類,聲明接受哪一類訪問者訪問,程式上是通過 accept 方法中的參數來定義的。
_ConcreteElement——具體元素:_實現 accept 方法,通常是 visitor.visit(this),基本上都形成了一種模式了。
_ObjectStruture——結構對象:_元素產生者,一般容納在多個不同類、不同介面的容器,如 List、Set、Map 等,在項目中,一般很少抽象出這個角色。
程式碼示例
`public class CompensationVisitor implements Visitor {`
`@Override`
`public void Visit(Element element) {`
`// TODO Auto-generated method stub`
`Employee employee = ((Employee) element);`
`System.out.println(`
`employee.getName() + "'s Compensation is " + (employee.getDegree() * employee.getVacationDays() * 10));`
`}`
`}`
狀態模式
概念
當一個對象內在狀態改變時允許其改變行為,這個對象看起來像改變了其類。
使用場景
-
行為隨狀態改變而改變的場景,這也是狀態模式的根本出發點,例如許可權設計,人員的狀態不同即使執行相同的行為結果也會不同,在這種情況下需要考慮使用狀態模式。
-
條件、分支判斷語句的替代者
結構
_State——抽象狀態角色:_介面或抽象類,負責對象狀態定義,並且封裝環境角色以實現狀態切換。
_ConcreteState——具體狀態角色:_每一個具體狀態必須完成兩個職責:本狀態的行為管理以及趨向狀態處理,通俗地說,就是本狀態下要做的事情,以及本狀態如何過渡到其他狀態。
_Context——環境角色:_定義客戶端需要的介面,並且負責具體狀態的切換。
程式碼示例
`//抽象狀態角色`
`public abstract class State {`
`//定義一個環境角色,提供子類訪問`
`protected Context context;`
`//設置環境角色`
`public void setContext(Context _context){`
`this.context = _context;`
`}`
`//行為1`
`public abstract void handle1();`
`//行為2`
`public abstract void handle2();`
`}`
解釋器模式
概念
給定一門語言,定義它的文法的一種表示,並定義一個解釋器,該解釋器使用該表示來解釋語言中的句子。
使用場景
-
重複發生的問題可以使用解釋器模式
-
一個簡單語法需要解釋的場景
結構
_AbstractExpression——抽象解釋器:_具體的解釋任務由各個實現類完成,具體的解釋器分別由TerminalExpression 和 Non-terminalExpression 完成。
_TerminalExpression——終結符表達式:_實現與文法中的元素相關聯的解釋操作,通常一個解釋器模式中只有一個終結符表達式,但有多個實例,對應不同的終結符。
_NonterminalExpression——非終結符表達式:_文法中的每條規則對應於一個非終結表達式,非終結符表達式根據邏輯的複雜程度而增加,原則上每個文法規則都對應一個非終結符表達式。
_Context——環境角色:_一般是用來存放文法中各個終結符所對應的具體值,這些資訊需要存放到環境角色中,很多情況下我們使用 Map 來充當環境角色就足夠了。
程式碼示例
`/**`
`* 終結符表達式`
`*/`
`public class TerminalExpression extends AbstractExpression {`
`@Override`
`public void interpret(Context ctx) {`
`// 實現與語法規則中的終結符相關聯的解釋操作`
`}`
`}`
`/**`
`* 非終結符表達式`
`*/`
`public class NonterminalExpression extends AbstractExpression {`
`@Override`
`public void interpret(Context ctx) {`
`// 實現與語法規則中的非終結符相關聯的解釋操作`
`}`
`}`
享元模式
概念
使用共享對象可有效地支援大量的細粒度的對象。
對象的資訊分為兩個部分:內部狀態(intrinsic)與外部狀態(extrinsic)。
_內部狀態:_內部狀態是對象可共享出來的資訊,存儲在享元對象內部並且不會隨環境改變而改變。
_外部狀態:_外部狀態是對象得以依賴的一個標記,是隨環境改變而改變的、不可以共享的狀態。
使用場景
-
系統中存在大量的相似對象。
-
細粒度的對象都具備較接近的外部狀態,而且內部狀態與環境無關,也就是說對象沒有特定身份。
-
需要緩衝池的場景。
結構
_Flyweight——抽象享元角色:_它簡單地說就是一個產品的抽象類,同時定義出對象的外部狀態和內部狀態的介面或實現。
_ConcreteFlyweight——具體享元角色:_具體的一個產品類,實現抽象角色定義的業務。該角色中需要注意的是內部狀態處理應該與環境無關,不應該出現一個操作改變了內部狀態,同時修改了外部狀態,這是絕對不允許的。
_unsharedConcreteFlyweight——不可共享的享元角色:_不存在外部狀態或者安全要求(如執行緒安全)不能夠使用共享技術的對象,該對象一般不會出現在享元工廠中。
_FlyweightFactory——享元工廠:_職責非常簡單,就是構造一個池容器,同時提供從池中獲得對象的方法。
程式碼示例
`public class FlyweightFactory {`
`//定義一個池容器`
`private static HashMap<String,Flyweight> pool= new`
`HashMap<String,Flyweight>();`
`//享元工廠`
`public static Flyweight getFlyweight(String Extrinsic){`
`//需要返回的對象`
`Flyweight flyweight = null;`
`//在池中沒有該對象`
`if(pool.containsKey(Extrinsic)){`
`flyweight = pool.get(Extrinsic);`
`}else{`
`//根據外部狀態創建享元對象`
`flyweight = new ConcreteFlyweight1(Extrinsic);`
`//放置到池中`
`pool.put(Extrinsic, flyweight);`
`}`
`return flyweight;`
`}`
`}`
橋樑模式
概念
將抽象和實現解耦,使得兩者可以獨立地變化。
使用場景
-
不希望或不適用使用繼承的場景
-
介面或抽象類不穩定的場景
-
重用性要求較高的場景
結構
_Abstraction——抽象化角色:_它的主要職責是定義出該角色的行為,同時保存一個對實現化角色的引用,該角色一般是抽象類。
_Implementor——實現化角色:_它是介面或者抽象類,定義角色必需的行為和屬性。
_RefinedAbstraction——修正抽象化角色:_它引用實現化角色對抽象化角色進行修正。
_ConcreteImplementor——具體實現化角色:_它實現介面或抽象類定義的方法和屬性。
程式碼示例
`public abstract class Abstraction {`
`//定義對實現化角色的引用`
`private Implementor imp;`
`//約束子類必須實現該構造函數`
`public Abstraction(Implementor _imp){`
`this.imp = _imp;`
`}`
`//自身的行為和屬性`
`public void request(){`
`this.imp.doSomething();`
`}`
`//獲得實現化角色`
`public Implementor getImp(){`
`return imp;`
`}`
`}`
總結
大家在學習設計模式的時候,不要把它看得有多難,歸根結底,都是一些概論性的總結。需要我們在平時的不斷學習和工作中,慢慢去理解它的深層原理,這樣才能靈活應用每一種設計模式。
設計模式是在前人的總結上,對一些場景的問題的進行解決的一種方案,設計模式不是公式,沒必要去死記硬背每一種模式,更重要的是了解它的抽象思想,以及應用設計模式怎麼更好的解決問題,可以達成什麼效果。理論雖多,但是我們要把它掌握的話,對於我們的實際開發來說會解決不少的問題。
當然後面還會繼續為大家更新關於設計原則的內容,方便大家進一步理解設計模式。