?

1.單例模式(Singleton Pattern)

定義:Ensure a class has only one instance, and provide a global point of access to it.(確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例。)

通用代碼:(是線程安全的)

public class Singleton {

private static final Singleton singleton = new Singleton(); //限制產生多個對象

private Singleton(){}//通過該方法獲得實例對象public static Singleton getSingleton(){return singleton;}//類中其他方法,盡量是 staticpublic static void doSomething(){}

}

使用場景:

● 要求生成唯一序列號的環境;

● 在整個項目中需要一個共享訪問點或共享數據,例如一個 Web 頁面上的計數 器,可以不用把每次刷新都記錄到資料庫中,使用單例模式保持計數器的值,並確 保是線程安全的;

● 創建一個對象需要消耗的資源過多,如要訪問 IO 和資料庫等資源;

● 需要定義大量的靜態常量和靜態方法(如工具類)的環境,可以採用單例模式 (當然,也可以直接聲明為 static 的方式)。

線程不安全實例

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 來實現。最優的辦法是如通用代碼那樣寫。

2.工廠模式

定義:Define an interface for creating an object,but let subclasses decide which class to instantiate.Factory Method lets a class defer instantiation to subclasses.

(定義一個用於創建對象的介面,讓子類決定實例化哪一個類。工廠方法使一個類 的實例化延遲到其子類。)

Product 為抽象產品類負責定義產品的共性,實現對事物最抽象的定義; Creator 為抽象創建類,也就是抽象工廠,具體如何創建產品類是由具體的實現工 廠 ConcreteCreator 完成的。

具體工廠類代碼

public class ConcreteCreator extends Creator {

public T createProduct(Class c){Product product=null;try {product =

(Product)Class.forName(c.getName()).newInstance();

} catch (Exception e) {//異常處理}return (T)product;}}

簡單工廠模式:

一個模塊僅需要一個工廠類,沒有必要把它產生出來,使用靜態的方法

多個工廠類:

每個人種(具體的產品類)都對應了一個創建者,每個創建者獨立負責創建對應的 產品對象,非常符合單一職責原則

代替單例模式:

單例模式的核心要求就是在內存中只有一個對象,通過工廠方法模式也可以只在內 存中生產一個對象

延遲初始化:

ProductFactory 負責產品類對象的創建工作,並且通過prMap 變數產生一個緩 存,對需要再次被重用的對象保留

使用場景:jdbc 連接資料庫,硬體訪問,降低對象的產生和銷毀

3.抽象工廠模式(Abstract Factory Pattern)

定義:Provide an interface for creating families of related or dependent objects without specifying their concrete classes.(為創建一組相關或相互依賴的對象提供 一個介面,而且無須指定它們的具體類。)

抽象工廠模式通用類圖:

抽象工廠模式通用源碼類圖:

抽象工廠類代碼

public abstract class AbstractCreator {

//創建 A 產品家族public abstract AbstractProductA createProductA(); //創建 B 產品家族public abstract AbstractProductB createProductB(); }

使用場景:

一個對象族(或是一組沒有任何關係的對象)都有相同的約束。 涉及不同操作系統的時候,都可以考慮使用抽象工廠模式

4.模板方法模式(Template Method Pattern)

定義:Define the skeleton of an algorithm in an operation,deferring some steps to subclasses.Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithms structure.(定義一個操作中的演算法的框 架,而將一些步驟延遲到子類中。使得子類可以不改變一個演算法的結構即可重定義 該演算法的某些特定步驟。)

AbstractClass 叫做抽象模板,它的方法分為兩類:

● 基本方法

基本方法也叫做基本操作,是由子類實現的方法,並且在模板方法被調用。

● 模板方法

可以有一個或幾個,一般是一個具體方法,也就是一個框架,實現對基本方法的調 度,完成固定的邏輯。

注意: 為了防止惡意的操作,一般模板方法都加上 final 關鍵字,不允許被覆 寫。

具體模板:ConcreteClass1 和ConcreteClass2 屬於具體模板,實現父類所定義的 一個或多個抽象方法,也就是父類定義的基本方法在子類中得以實現

使用場景:

● 多個子類有公有的方法,並且邏輯基本相同時。

● 重要、複雜的演算法,可以把核心演算法設計為模板方法,周邊的相關細節功能則由 各個子類實現。

● 重構時,模板方法模式是一個經常使用的模式,把相同的代碼抽取到父類中,然 後通過鉤子函數(見「模板方法模式的擴展」)約束其行為。

5.建造者模式(Builder Pattern)

定義:Separate the construction of a complex object from its representation so that the same construction process can create different representations.(將一個 複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。)

● Product 產品類

通常是實現了模板方法模式,也就是有模板方法和基本方法,例子中的 BenzModel 和 BMWModel 就屬於產品類。

● Builder 抽象建造者

規範產品的組建,一般是由子類實現。例子中的 CarBuilder 就屬於抽象建造者。 ● ConcreteBuilder 具體建造者

實現抽象類定義的所有方法,並且返回一個組建好的對象。例子中的 BenzBuilder 和 BMWBuilder 就屬於具體建造者。

● Director 導演類

負責安排已有模塊的順序,然後告訴 Builder 開始建造

使用場景:

● 相同的方法,不同的執行順序,產生不同的事件結果時,可以採用建造者模式。 ● 多個部件或零件,都可以裝配到一個對象中,但是產生的運行結果又不相同時, 則可以使用該模式。

● 產品類非常複雜,或者產品類中的調用順序不同產生了不同的效能,這個時候使 用建造者模式非常合適。

建造者模式與工廠模式的不同:

建造者模式最主要的功能是基本方法的調用順序安排,這些基本方法已經實現了, 順序不同產生的對象也不同;

工廠方法則重點是創建,創建零件是它的主要職責,組裝順序則不是它關心的。

6.代理模式(Proxy Pattern)

定義:Provide a surrogate or placeholder for another object to control access to it. (為其他對象提供一種代理以控制對這個對象的訪問。)

● Subject 抽象主題角色

抽象主題類可以是抽象類也可以是介面,是一個最普通的業務類型定義,無特殊要 求。

● RealSubject 具體主題角色

也叫做被委託角色、被代理角色。它才是冤大頭,是業務邏輯的具體執行者。 ● Proxy 代理主題角色

也叫做委託類、代理類。它負責對真實角色的應用,把所有抽象主題類定義的方法 限制委託給真實主題角色實現,並且在真實主題角色處理完畢前後做預處理和善後 處理工作。

普通代理和強制代理:

普通代理就是我們要知道代理的存在,也就是類似的 GamePlayerProxy 這個類的 存在,然後才能訪問;

強制代理則是調用者直接調用真實角色,而不用關心代理是否存在,其代理的產生 是由真實角色決定的。

普通代理: 在該模式下,調用者只知代理而不用知道真實的角色是誰,屏蔽了真實角色的變更 對高層模塊的影響,真實的主題角色想怎麼修改就怎麼修改,對高層次的模塊沒有 任何的影響,只要你實現了介面所對應的方法,該模式非常適合對擴展性要求較高 的場合。

強制代理: 強制代理的概念就是要從真實角色查找到代理角色,不允許直接訪問真實角色。高 層模塊只要調用 getProxy 就可以訪問真實角色的所有方法,它根本就不需要產生 一個代理出來,代理的管理已經由真實角色自己完成。

動態代理: 根據被代理的介面生成所有的方法,也就是說給定一個介面,動態代理會宣稱「我 已經實現該介面下的所有方法了」。

兩條獨立發展的線路。動態代理實現代理的職責,業務邏輯 Subject 實現相關的邏 輯功能,兩者之間沒有必然的相互耦合的關係。通知 Advice 從另一個切面切入, 最終在高層模塊也就是 Client 進行耦合,完成邏輯的封裝任務。

動態代理調用過程示意圖:

動態代理的意圖:橫切面編程,在不改變我們已有代碼結構的情況下增強或控制對 象的行為。

首要條件:被代理的類必須要實現一個介面。

7.原型模式(Prototype Pattern)

定義:Specify the kinds of objects to create using a prototypical instance,and create new objects by copying this prototype.(用原型實例指定創建對象的種類, 並且通過拷貝這些原型創建新的對象。)

原型模式通用代碼:

public class PrototypeClass implements Cloneable{

//覆寫父類 Object 方法

@Override

public PrototypeClass clone(){

PrototypeClass prototypeClass = null;

try {

prototypeClass = (PrototypeClass)super.clone(); } catch (CloneNotSupportedException e) {

//異常處理

}

return prototypeClass;

}

}

原型模式實際上就是實現 Cloneable 介面,重寫 clone()方法。

使用原型模式的優點:

● 性能優良

原型模式是在內存二進位流的拷貝,要比直接 new 一個對象性能好很多,特別是 要在一個循環體內產生大量的對象時,原型模式可以更好地體現其優點。

● 逃避構造函數的約束

這既是它的優點也是缺點,直接在內存中拷貝,構造函數是不會執行的(參見 13.4 節)。

使用場景:

● 資源優化場景

類初始化需要消化非常多的資源,這個資源包括數據、硬體資源等。

● 性能和安全要求的場景

通過 new 產生一個對象需要非常繁瑣的數據準備或訪問許可權,則可以使用原型模 式。

● 一個對象多個修改者的場景

一個對象需要提供給其他對象訪問,而且各個調用者可能都需要修改其值時,可以 考慮使用原型模式拷貝多個對象供調用者使用。

淺拷貝和深拷貝:

淺拷貝:Object 類提供的方法 clone 只是拷貝本對象,其對象內部的數組、引用 對象等都不拷貝,還是指向原生對象的內部元素地址,這種拷貝就叫做淺拷貝,其 他的原始類型比如 int、long、char、string(當做是原始類型)等都會被拷貝。 注意: 使用原型模式時,引用的成員變數必須滿足兩個條件才不會被拷貝:一是 類的成員變數,而不是方法內變數;二是必須是一個可變的引用對象,而不是一個 原始類型或不可變對象。

深拷貝:對私有的類變數進行獨立的拷貝

如:thing.arrayList = (ArrayList<String>)this.arrayList.clone();

8.中介者模式

定義:Define an object that encapsulates how a set of objects interact.Mediator promotes loose coupling by keeping objects from referring to each other explicitly,and it lets you vary their interaction independently.(用一個中介對象封裝 一系列的對象交互,中介者使各對象不需要顯示地相互作用,從而使其耦合鬆散, 而且可以獨立地改變它們之間的交互。)

● Mediator 抽象中介者角色

抽象中介者角色定義統一的介面,用於各同事角色之間的通信。

● Concrete Mediator 具體中介者角色

具體中介者角色通過協調各同事角色實現協作行為,因此它必須依賴於各個同事角 色。

● Colleague 同事角色

每一個同事角色都知道中介者角色,而且與其他的同事角色通信的時候,一定要通 過中介者角色協作。每個同事類的行為分為兩種:一種是同事本身的行為,比如改 變對象本身的狀態,處理自己的行為等,這種行為叫做自發行為(Self- Method),與其他的同事類或中介者沒有任何的依賴;第二種是必須依賴中介者 才能完成的行為,叫做依賴方法(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();}

ps:使用同事類注入而不使用抽象注入的原因是因為抽象類中不具有每個同事類 必須要完成的方法。即每個同事類中的方法各不相同。

:為什麼同事類要使用構造函數注入中介者,而中介者使用 getter/setter 方式注 入同事類呢?

這是因為同事類必須有中介者,而中介者卻可以只有部分同事類。

使用場景: 中介者模式適用於多個對象之間緊密耦合的情況,緊密耦合的標準是:在類圖中出 現了蜘蛛網狀結構,即每個類都與其他的類有直接的聯繫。

9.命令模式

定義:Encapsulate a request as an object,thereby letting you parameterize clients with different requests,queue or log requests,and support undoable operations.

(將一個請求封裝成一個對象,從而讓你使用不同的請求把客戶端參數化,對請求 排隊或者記錄請求日誌,可以提供命令的撤銷和恢復功能。)

● Receive 接收者角色

該角色就是幹活的角色,命令傳遞到這裡是應該被執行的,具體到我們上面的例子 中就是 Group 的三個實現類(需求組,美工組,代碼組)。

● Command 命令角色

需要執行的所有命令都在這裡聲明。

● Invoker 調用者角色

接收到命令,並執行命令。在例子中,我(項目經理)就是這個角色。

使用場景:

認為是命令的地方就可以採用命令模式,例如,在 GUI 開發中,一個按鈕的點擊 是一個命令,可以採用命令模式;模擬 DOS 命令的時候,當然也要採用命令模 式;觸發-反饋機制的處理等。

10.責任鏈模式

定義:Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request.Chain the receiving objects and pass the request along the chain until an object handles it.(使多個對象都有機會處理請 求,從而避免了請求的發送者和接受者之間的耦合關係。將這些對象連成一條鏈, 並沿著這條鏈傳遞該請求,直到有對象處理它為止。)

抽象處理者的代碼:

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);}

抽象的處理者實現三個職責:

一是定義一個請求的處理方法 handleMessage,唯一對外開放的方法;

二是定義一個鏈的編排方法 setNext,設置下一個處理者;

三是定義了具體的請求者必須實現的兩個方法:定義自己能夠處理的級別 getHandlerLevel 和具體的處理任務 echo。

注意事項:

鏈中節點數量需要控制,避免出現超長鏈的情況,一般的做法是在 Handler 中設置 一個最大節點數量,在 setNext 方法中判斷是否已經是超過其閾值,超過則不允許 該鏈建立,避免無意識地破壞系統性能。

11.裝飾模式(Decorator Pattern)

定義:Attach additional responsibilities to an object dynamically keeping the same interface.Decorators provide a flexible alternative to subclassing for extending functionality.(動態地給一個對象添加一些額外的職責。就增加功能來 說,裝飾模式相比生成子類更為靈活。)

● Component 抽象構件

Component 是一個介面或者是抽象類,就是定義我們最核心的對象,也就是最原 始的對象,如上面的成績單。

注意:在裝飾模式中,必然有一個最基本、最核心、最原始的介面或抽象類充當 Component 抽象構件。

● ConcreteComponent 具體構件

ConcreteComponent 是最核心、最原始、最基本的介面或抽象類的實現,你要裝 飾的就是它。

● Decorator 裝飾角色

一般是一個抽象類,做什麼用呢?實現介面或者抽象方法,它裡面可不一定有抽象 的方法呀,在它的屬性里必然有一個 private 變數指向 Component 抽象構件。 ● 具體裝飾角色

ConcreteDecoratorA 和 ConcreteDecoratorB 是兩個具體的裝飾類,你要把你最核 心的、最原始的、最基本的東西裝飾成其他東西,上面的例子就是把一個比較平庸 的成績單裝飾成家長認可的成績單。

使用場景:

● 需要擴展一個類的功能,或給一個類增加附加功能。

● 需要動態地給一個對象增加功能,這些功能可以再動態地撤銷。

● 需要為一批的兄弟類進行改裝或加裝功能,當然是首選裝飾模式。

12.策略模式(Strategy Pattern)

定義:Define a family of algorithms,encapsulate each one,and make them interchangeable.(定義一組演算法,將每個演算法都封裝起來,並且使它們之間可以 互換。)

● Context 封裝角色

它也叫做上下文角色,起承上啟下封裝作用,屏蔽高層模塊對策略、演算法的直接訪 問,封裝可能存在的變化。

● Strategy 抽象策略角色

策略、演算法家族的抽象,通常為介面,定義每個策略或演算法必須具有的方法和屬 性。各位看官可能要問了,類圖中的 AlgorithmInterface 是什麼意思,嘿嘿, algorithm 是「運演算法則」的意思,結合起來意思就明白了吧。

● ConcreteStrategy 具體策略角色(多個)

實現抽象策略中的操作,該類含有具體的演算法。

使用場景:

● 多個類只有在演算法或行為上稍有不同的場景。

● 演算法需要自由切換的場景。

● 需要屏蔽演算法規則的場景。

注意事項:具體策略數量超過 4 個,則需要考慮使用混合模式

策略模式擴展:策略枚舉

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); }

定義:

● 它是一個枚舉。

● 它是一個濃縮了的策略模式的枚舉。

注意:

受枚舉類型的限制,每個枚舉項都是 public、final、static 的,擴展性受到了一定 的約束,因此在系統開發中,策略枚舉一般擔當不經常發生變化的角色。 致命缺陷:

所有的策略都需要暴露出去,由客戶端決定使用哪一個策略。

13.適配器模式(Adapter Pattern)

定義:Convert the interface of a class into another interface clients

expect.Adapter lets classes work together that couldnt otherwise because of incompatible interfaces.(將一個類的介面變換成客戶端所期待的另一種介面,從 而使原本因介面不匹配而無法在一起工作的兩個類能夠在一起工作。)

類適配器:

● Target 目標角色

該角色定義把其他類轉換為何種介面,也就是我們的期望介面,例子中的 IUserInfo 介面就是目標角色。

● Adaptee 源角色

你想把誰轉換成目標角色,這個「誰」就是源角色,它是已經存在的、運行良好的類 或對象,經過適配器角色的包裝,它會成為一個嶄新、靚麗的角色。

● Adapter 適配器角色

適配器模式的核心角色,其他兩個角色都是已經存在的角色,而適配器角色是需要 新建立的,它的職責非常簡單:把源角色轉換為目標角色,怎麼轉換?通過繼承或 是類關聯的方式。

使用場景:

你有動機修改一個已經投產中的介面時,適配器模式可能是最適合你的模式。比如 系統擴展了,需要使用一個已有或新建立的類,但這個類又不符合系統的介面,怎 么辦?使用適配器模式,這也是我們例子中提到的。

注意事項:

詳細設計階段不要考慮使用適配器模式,使用主要場景為擴展應用中。

對象適配器:

對象適配器和類適配器的區別:

類適配器是類間繼承,對象適配器是對象的合成關係,也可以說是類的關聯關係, 這是兩者的根本區別。(實際項目中對象適配器使用到的場景相對比較多)。

14.迭代器模式(Iterator Pattern)

定義:Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.(它提供一種方法訪問一個容器對 象中各個元素,而又不需暴露該對象的內部細節。)

● Iterator 抽象迭代器

抽象迭代器負責定義訪問和遍曆元素的介面,而且基本上是有固定的 3 個方法: first()獲得第一個元素,next()訪問下一個元素,isDone()是否已經訪問到底部

(Java 叫做 hasNext()方法)。

● ConcreteIterator 具體迭代器

具體迭代器角色要實現迭代器介面,完成容器元素的遍歷。

● Aggregate 抽象容器

容器角色負責提供創建具體迭代器角色的介面,必然提供一個類似 createIterator() 這樣的方法,在 Java 中一般是 iterator()方法。

● Concrete Aggregate 具體容器

具體容器實現容器介面定義的方法,創建出容納迭代器的對象。

ps:迭代器模式已經被淘汰,java 中已經把迭代器運用到各個聚集類

(collection)中了,使用 java 自帶的迭代器就已經滿足我們的需求了。

15.組合模式((Composite Pattern))

定義:Compose objects into tree structures to represent part-whole hierarchies.Composite lets clients treat individual objects and compositions of objects uniformly.(將對象組合成樹形結構以表示「部分-整體」的層次結構,使得用 戶對單個對象和組合對象的使用具有一致性。)

● Component 抽象構件角色

定義參加組合對象的共有方法和屬性,可以定義一些默認的行為或屬性,比如我們 例子中的 getInfo 就封裝到了抽象類中。

● Leaf 葉子構件

葉子對象,其下再也沒有其他的分支,也就是遍歷的最小單位。

● Composite 樹枝構件

樹枝對象,它的作用是組合樹枝節點和葉子節點形成一個樹形結構。

樹枝構件的通用代碼:

public class Composite extends Component {

//構件容器private ArrayList componentArrayList = newArrayList();//增加一個葉子構件或樹枝構件public void add(Component component){this.componentArrayList.add(component);}//刪除一個葉子構件或樹枝構件public void remove(Component component){this.componentArrayList.remove(component);}//獲得分支下的所有葉子構件和樹枝構件public ArrayList getChildren(){return this.componentArrayList;}}

使用場景:

● 維護和展示部分-整體關係的場景,如樹形菜單、文件和文件夾管理。

● 從一個整體中能夠獨立出部分模塊或功能的場景。

注意:

只要是樹形結構,就考慮使用組合模式。

16.觀察者模式(Observer Pattern)

定義:Define a one-to-many dependency between objects so that when one object changes state,all its dependents are notified and updated automatically.

(定義對象間一種一對多的依賴關係,使得每當一個對象改變狀態,則所有依賴於 它的對象都會得到通知並被自動更新。)

● Subject 被觀察者

定義被觀察者必須實現的職責,它必須能夠動態地增加、取消觀察者。它一般是抽 象類或者是實現類,僅僅完成作為被觀察者必須實現的職責:管理觀察者並通知觀 察者。

● Observer 觀察者

觀察者接收到消息後,即進行 update(更新方法)操作,對接收到的信息進行處 理。

● ConcreteSubject 具體的被觀察者

定義被觀察者自己的業務邏輯,同時定義對哪些事件進行通知。

● ConcreteObserver 具體的觀察者

每個觀察在接收到消息後的處理反應是不同,各個觀察者有自己的處理邏輯。

被觀察者通用代碼:

public abstract class Subject {

//定義一個觀察者數組private Vector obsVector = new Vector();//增加一個觀察者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();}}}

使用場景:

● 關聯行為場景。需要注意的是,關聯行為是可拆分的,而不是「組合」關係。

● 事件多級觸發場景。

● 跨系統的消息交換場景,如消息隊列的處理機制。

注意:

● 廣播鏈的問題

在一個觀察者模式中最多出現一個對象既是觀察者也是被觀察者,也就是說消息最 多轉發一次(傳遞兩次)。

● 非同步處理問題

觀察者比較多,而且處理時間比較長,採用非同步處理來考慮線程安全和隊列的問 題。

17.門面模式(Facade Pattern)

定義:Provide a unified interface to a set of interfaces in a subsystem.Facade defines a higher-level interface that makes the subsystem easier to use.(要求一 個子系統的外部與其內部的通信必須通過一個統一的對象進行。門面模式提供一個 高層次的介面,使得子系統更易於使用。)

● Facade 門面角色

客戶端可以調用這個角色的方法。此角色知曉子系統的所有功能和責任。一般情況 下,本角色會將所有從客戶端發來的請求委派到相應的子系統去,也就說該角色沒 有實際的業務邏輯,只是一個委託類。

● subsystem 子系統角色

可以同時有一個或者多個子系統。每一個子系統都不是一個單獨的類,而是一個類 的集合。子系統並不知道門面的存在。對於子系統而言,門面僅僅是另外一個客戶 端而已。

使用場景:

● 為一個複雜的模塊或子系統提供一個供外界訪問的介面

● 子系統相對獨立——外界對子系統的訪問只要黑箱操作即可

● 預防低水平人員帶來的風險擴散

注意:

●一個子系統可以有多個門面

●門面不參與子系統內的業務邏輯

18.備忘錄模式(Memento Pattern)

定義:Without violating encapsulation,capture and externalize an objects internal state so that the object can be restored to this state later.(在不破壞封裝性的前提 下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態。這樣以後就可將該 對象恢復到原先保存的狀態。)

● Originator 發起人角色

記錄當前時刻的內部狀態,負責定義哪些屬於備份範圍的狀態,負責創建和恢復備 忘錄數據。

● Memento 備忘錄角色(簡單的 javabean)

負責存儲 Originator 發起人對象的內部狀態,在需要的時候提供發起人需要的內部 狀態。

● Caretaker 備忘錄管理員角色(簡單的 javabean)

對備忘錄進行管理、保存和提供備忘錄。

使用場景:

● 需要保存和恢複數據的相關狀態場景。

● 提供一個可回滾(rollback)的操作。

● 需要監控的副本場景中。

● 資料庫連接的事務管理就是用的備忘錄模式。

注意:

●備忘錄的生命期

●備忘錄的性能

不要在頻繁建立備份的場景中使用備忘錄模式(比如一個for 循環中)。

clone 方式備忘錄:

● 發起人角色融合了發起人角色和備忘錄角色,具有雙重功效

多狀態的備忘錄模式

● 增加了一個 BeanUtils 類,其中 backupProp 是把發起人的所有屬性值轉換到 HashMap 中,方便備忘錄角色存儲。restoreProp 方法則是把HashMap 中的值返 回到發起人角色中。

BeanUtil 工具類代碼:

public class BeanUtils {

//把 bean 的所有屬性及數值放入到 Hashmap 中public static HashMap backupProp(Object bean){HashMap result = newHashMap();try {//獲得 Bean 描述BeanInfobeanInfo=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,newObject[]{});if(!fieldName.equalsIgnoreCase("class")){result.put(fieldName, fieldValue);}}} catch (Exception e) {//異常處理}return result;}//把 HashMap 的值返回到 bean 中public static void restoreProp(Object bean,HashMap 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, newObject[]{propMap.get(fieldName)});}}} catch (Exception e) {//異常處理System.out.println("shit"); e.printStackTrace();}}}

多備份的備忘錄:略

封裝得更好一點:保證只能對發起人可讀

●建立一個空介面 IMemento——什麼方法屬性都沒有的介面,然後在發起人 Originator 類中建立一個內置類(也叫做類中類)Memento 實現IMemento 介面, 同時也實現自己的業務邏輯。

19.訪問者模式(Visitor Pattern)

定義:Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates. (封裝一些作用於某種數據結構中的各元素的 操作,它可以在不改變數據結構的前提下定義作用於這些元素的新的操作。)

● Visitor——抽象訪問者

抽象類或者介面,聲明訪問者可以訪問哪些元素,具體到程序中就是 visit 方法的 參數定義哪些對象是可以被訪問的。

● ConcreteVisitor——具體訪問者

它影響訪問者訪問到一個類後該怎麼干,要做什麼事情。

● Element——抽象元素

介面或者抽象類,聲明接受哪一類訪問者訪問,程序上是通過 accept 方法中的參 數來定義的。

● ConcreteElement——具體元素

實現 accept 方法,通常是 visitor.visit(this),基本上都形成了一種模式了。 ● ObjectStruture——結構對象

元素產生者,一般容納在多個不同類、不同介面的容器,如 List、Set、Map 等, 在項目中,一般很少抽象出這個角色。

使用場景:

● 一個對象結構包含很多類對象,它們有不同的介面,而你想對這些對象實施一些 依賴於其具體類的操作,也就說是用迭代器模式已經不能勝任的情景。

● 需要對一個對象結構中的對象進行很多不同並且不相關的操作,而你想避免讓這 些操作「污染」這些對象的類。

20.狀態模式(複雜)

定義:Allow an object to alter its behavior when its internal state changes.The object will appear to change its class.(當一個對象內在狀態改變時允許其改變行 為,這個對象看起來像改變了其類。)

● State——抽象狀態角色

介面或抽象類,負責對象狀態定義,並且封裝環境角色以實現狀態切換。

● ConcreteState——具體狀態角色

每一個具體狀態必須完成兩個職責:本狀態的行為管理以及趨向狀態處理,通俗地 說,就是本狀態下要做的事情,以及本狀態如何過渡到其他狀態。

● Context——環境角色

定義客戶端需要的介面,並且負責具體狀態的切換。

使用場景:

● 行為隨狀態改變而改變的場景

這也是狀態模式的根本出發點,例如許可權設計,人員的狀態不同即使執行相同的行 為結果也會不同,在這種情況下需要考慮使用狀態模式。

● 條件、分支判斷語句的替代者

注意:

狀態模式適用於當某個對象在它的狀態發生改變時,它的行為也隨著發生比較大的 變化,也就是說在行為受狀態約束的情況下可以使用狀態模式,而且使用時對象的 狀態最好不要超過 5 個。

21.解釋器模式(Interpreter Pattern) (少用)

定義:Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.

(給定一門語言,定義它的文法的一種表示,並定義一個解釋器,該解釋器使用該 表示來解釋語言中的句子。)

● AbstractExpression——抽象解釋器

具體的解釋任務由各個實現類完成,具體的解釋器分別由 TerminalExpression 和 Non-terminalExpression 完成。

● TerminalExpression——終結符表達式

實現與文法中的元素相關聯的解釋操作,通常一個解釋器模式中只有一個終結符表 達式,但有多個實例,對應不同的終結符。具體到我們例子就是 VarExpression 類,表達式中的每個終結符都在棧中產生了一個 VarExpression 對象。

● NonterminalExpression——非終結符表達式

文法中的每條規則對應於一個非終結表達式,具體到我們的例子就是加減法規則分 別對應到 AddExpression 和 SubExpression 兩個類。非終結符表達式根據邏輯的 複雜程度而增加,原則上每個文法規則都對應一個非終結符表達式。

● Context——環境角色

具體到我們的例子中是採用 HashMap 代替。

使用場景:

● 重複發生的問題可以使用解釋器模式

● 一個簡單語法需要解釋的場景

注意:

盡量不要在重要的模塊中使用解釋器模式,否則維護會是一個很大的問題。在項目 中可以使用 shell、JRuby、Groovy 等腳本語言來代替解釋器模式,彌補Java 編 譯型語言的不足。

22.享元模式(Flyweight Pattern)

定義:Use sharing to support large numbers of fine-grained objects efficiently. (使用共享對象可有效地支持大量的細粒度的對象。)

對象的信息分為兩個部分:內部狀態(intrinsic)與外部狀態(extrinsic)。 ● 內部狀態

內部狀態是對象可共享出來的信息,存儲在享元對象內部並且不會隨環境改變而改 變。

● 外部狀態

外部狀態是對象得以依賴的一個標記,是隨環境改變而改變的、不可以共享的狀 態。

● Flyweight——抽象享元角色

它簡單地說就是一個產品的抽象類,同時定義出對象的外部狀態和內部狀態的介面 或實現。

● ConcreteFlyweight——具體享元角色

具體的一個產品類,實現抽象角色定義的業務。該角色中需要注意的是內部狀態處 理應該與環境無關,不應該出現一個操作改變了內部狀態,同時修改了外部狀態, 這是絕對不允許的。

● unsharedConcreteFlyweight——不可共享的享元角色

不存在外部狀態或者安全要求(如線程安全)不能夠使用共享技術的對象,該對象 一般不會出現在享元工廠中。

● FlyweightFactory——享元工廠

職責非常簡單,就是構造一個池容器,同時提供從池中獲得對象的方法。

享元工廠的代碼:

public class FlyweightFactory {

//定義一個池容器private static HashMap pool= newHashMap();//享元工廠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;}}

使用場景:

● 系統中存在大量的相似對象。

● 細粒度的對象都具備較接近的外部狀態,而且內部狀態與環境無關,也就是說對 象沒有特定身份。

● 需要緩衝池的場景。

注意:

● 享元模式是線程不安全的,只有依靠經驗,在需要的地方考慮一下線程安全,在 大部分場景下不用考慮。對象池中的享元對象盡量多,多到足夠滿足為止。 ● 性能安全:外部狀態最好以 java 的基本類型作為標誌,如 String,int,可以提 高效率。

23.橋樑模式(Bridge Pattern)

定義:Decouple an abstraction from its implementation so that the two can vary independently.(將抽象和實現解耦,使得兩者可以獨立地變化。)

● Abstraction——抽象化角色

它的主要職責是定義出該角色的行為,同時保存一個對實現化角色的引用,該角色 一般是抽象類。

● Implementor——實現化角色

它是介面或者抽象類,定義角色必需的行為和屬性。

● RefinedAbstraction——修正抽象化角色

它引用實現化角色對抽象化角色進行修正。

● ConcreteImplementor——具體實現化角色

它實現介面或抽象類定義的方法和屬性。

使用場景:

● 不希望或不適用使用繼承的場景

● 介面或抽象類不穩定的場景

● 重用性要求較高的場景

注意:

發現類的繼承有 N 層時,可以考慮使用橋樑模式。橋樑模式主要考慮如何拆分抽 象和實現。

設計原則:

●Single Responsibility Principle:單一職責原則 單一職責原則有什麼好處:

● 類的複雜性降低,實現什麼職責都有清晰明確的定義;

● 可讀性提高,複雜性降低,那當然可讀性提高了;

● 可維護性提高,可讀性提高,那當然更容易維護了;

●變更引起的風險降低,變更是必不可少的,如果介面的單一職責做得好,一個接 口修改只對相應的實現類有影響,對其他的介面無影響,這對系統的擴展性、維護 性都有非常大的幫助。

ps:介面一定要做到單一職責,類的設計盡量做到只有一個原因引起變化。

單一職責原則提出了一個編寫程序的標準,用「職責」或「變化原因」來衡量介面 或類設計得是否優良,但是「職責」和「變化原因」都是不可度量的,因項目而異,因 環境而異。

● Liskov Substitution Principle:里氏替換原則

定義:Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.

(所有引用基類的地方必須能透明地使用其子類的對象。)

通俗點講,只要父類能出現的地方子類就可以出現,而且替換為子類也不會產生任 何錯誤或異常,使用者可能根本就不需要知道是父類還是子類。但是,反過來就不 行了,有子類出現的地方,父類未必就能適應。

定義中包含的四層含義:

1.子類必須完全實現父類的方法

2.子類可以有自己的個性

3.覆蓋或實現父類的方法時輸入參數可以被放大

如果父類的輸入參數類型大於子類的輸入參數類型,會出現父類存在的地 方,子類未必會存在,因為一旦把子類作為參數傳入,調用者很可能進入子類的方 法範疇。

4. 覆寫或實現父類的方法時輸出結果可以被縮小

父類的一個方法的返回值是一個類型 T,子類的相同方法(重載或覆寫)的返 回值為 S,那麼里氏替換原則就要求 S 必須小於等於 T,也就是說,要麼 S 和 T 是同一個類型,要麼 S 是T 的子類。

● Interface Segregation Principle:介面隔離原則

介面分為兩種:

實例介面(Object Interface):Java 中的類也是一種介面

類介面(Class Interface): Java 中經常使用Interface 關鍵字定義的介面

隔離:建立單一介面,不要建立臃腫龐大的介面;即介面要盡量細化,同時介面中 的方法要盡量少。

介面隔離原則與單一職責原則的不同:介面隔離原則與單一職責的審視角度是不相 同的,單一職責要求的是類和介面職責單一,注重的是職責,這是業務邏輯上的劃 分,而介面隔離原則要求介面的方法盡量少。

● Dependence Inversion Principle:依賴倒置原則

原始定義:

①高層模塊不應該依賴低層模塊,兩者都應該依賴其抽象;

②抽象不應該依賴細節(實現類);

③細節應該依賴抽象。

依賴倒置原則在 java 語言中的體現:

①模塊間的依賴通過抽象發生,實現類之間不發生直接的依賴關係,其依賴關係是 通過介面或抽象類產生的;

②介面或抽象類不依賴於實現類;

③實現類依賴介面或抽象類。

依賴的三種寫法:

①構造函數傳遞依賴對象(構造函數注入)

②Setter 方法傳遞依賴對象(setter 依賴注入)

③介面聲明依賴對象(介面注入)

使用原則:

依賴倒置原則的本質就是通過抽象(介面或抽象類)使各個類或模塊的實現彼此獨 立,不互相影響,實現模塊間的松耦合,我們怎麼在項目中使用這個規則呢?只要 遵循以下的幾個規則就可以:

①每個類盡量都有介面或抽象類,或者抽象類和介面兩者都具備

②變數的表面類型盡量是介面或者是抽象類

③任何類都不應該從具體類派生(只要不超過兩層的繼承是可以忍受的)

④盡量不要複寫基類的方法

⑤結合里氏替換原則使用

●Open Closed Principle:開閉原則

定義:軟體實體應該對擴展開放,對修改關閉。

其含義是說一個軟體實體應該通過擴展來實現變化,而不是通過修改已有的代碼來 實現變化。

軟體實體:項目或軟體產品中按照一定的邏輯規則劃分的模塊、抽象和類、方法。 變化的三種類型:

①邏輯變化

只變化一個邏輯,而不涉及其他模塊,比如原有的一個演算法是 a*b+c,現在需要修 改為 a*b*c,可以通過修改原有類中的方法的方式來完成,前提條件是所有依賴或 關聯類都按照相同的邏輯處理。

②子模塊變化

一個模塊變化,會對其他的模塊產生影響,特別是一個低層次的模塊變化必然引起 高層模塊的變化,因此在通過擴展完成變化時,高層次的模塊修改是必然的。 ③可見視圖變化

可見視圖是提供給客戶使用的界面,如 JSP 程序、Swing 界面等,該部分的變化 一般會引起連鎖反應(特別是在國內做項目,做歐美的外包項目一般不會影響太 大)。可以通過擴展來完成變化,這要看我們原有的設計是否靈活。

最後,歡迎做Java的工程師朋友關注小編,點關注,不迷路;持續更新Java架構相關技術及資訊!!!

需要Java進階學習資料或者面試資料的可以私信「Java」(視頻文檔都會免費分享,大家一起學習一起進步!)

?

?

提供免費的Java架構學習資料(裡面有高可用、高並發、高性能及分散式、Jvm性能調優、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)合理利用自己每一分每一秒的時間來學習提升自己,不要再用"沒有時間「來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代!


推薦閱讀:
相关文章