23種設計模式

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;
}
//類中其餘方法,儘可能是 static
public 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 extends Product> T createProduct(Class<T> 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 algorithm's 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 couldn't 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<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;
}
}使用場景:
● 維護和展現部分-總體關係的場景,如樹形菜單、文件和文件夾管理。
● 從一個總體中可以獨立出部分模塊或功能的場景。
注意:
只要是樹形結構,就考慮使用組合模式。
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<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();
}}
}
使用場景:
● 關聯行爲場景。須要注意的是,關聯行爲是可拆分的,而不是「組合」關係。
● 事件多級觸發場景。
● 跨系統的消息交換場景,如消息隊列的處理機制。
注意:
● 廣播鏈的問題
在一個觀察者模式中最多出現一個對象既是觀察者也是被觀察者,也就是說消息最
多轉發一次(傳遞兩次)。
● 異步處理問題
觀察者比較多,並且處理時間比較長,採用異步處理來考慮線程安全和隊列的問
題。
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 object's 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<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();
}
}
}
多備份的備忘錄:略
封裝得更好一點:保證只能對發起人可讀
●創建一個空接口 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<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;
}
}
使用場景:
● 系統中存在大量的類似對象。
● 細粒度的對象都具有較接近的外部狀態,並且內部狀態與環境無關,也就是說對
象沒有特定身份。
● 須要緩衝池的場景。
注意:
● 享元模式是線程不安全的,只有依靠經驗,在須要的地方考慮一下線程安全,在
大部分場景下不用考慮。對象池中的享元對象儘可能多,多到足夠知足爲止。
● 性能安全:外部狀態最好以 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 界面等,該部分的變化
通常會引發連鎖反應(特別是在國內作項目,作歐美的外包項目通常不會影響太
大)。能夠經過擴展來完成變化,這要看咱們原有的設計是否靈活。
相關文章
相關標籤/搜索