知識須要不斷積累、總結和沉澱,思考和寫做是成長的催化劑php
老生常談建立型一、Singleton單例二、Factory Method工廠方法三、Abstract Factory抽象工廠四、builder建造者五、Prototype原型結構型一、Adapter 適配器二、Decorator 裝飾三、Bridge 橋接四、Composite 組合五、Flyweight 享元六、Proxy 代理七、Facade外觀行爲型一、Interpreter 解釋器二、Template Method 模板方法三、Chain of Responsibility 責任鏈四、Command 命令五、Iterator 迭代器六、Mediator 中介者七、Memento備忘錄八、Observer 觀察者九、State 狀態十、Strategy 策略十一、Visitor 訪問者謝謝nginx
GOF23種設計模式,想必都有聽過或學習過,就是有四我的搞了一本書總結了在面向對象開發過程當中常見問題的解決方案。程序員
啥是模式?就是發現一種可識別的規律,好比色彩模式(簡歷模版也算)。模式也每每和抽象思惟有關,分析事物共性,抽取事物共同的部分來幫助人們認識規律。web
啥又是設計模式?就是對事物的從新定位整合來解決問題的一種模版一種套路。它是成熟的解決方案,解決相似的問題,這樣咱們能夠站在巨人的肩膀上,更加優雅的開發設計。正則表達式
(本手冊儘量採用口語化表達,便於代入,很基礎的細節會刪減,瞭解過設計模式者服用更佳,查漏溫故,瀏覽起來會比較輕鬆)算法
進入正題,設計模式按功能分爲三大類數據庫
建立型設計模式,顧名思義用於對象的建立。提供建立對象就是如何New一個對象
的通用方法。編程
單例模式使得一個類只有一個實例。一般像一些工具類,只須要初始化一個實例便可,不須要每次使用都去再實例化一個,這樣能夠解決些資源浪費。和靜態類功能上相似,只不過單例是個對象實例。套路1
:一般三步走,私有構造函數禁止外部構造,公開靜態方法提供給外部使用,私有靜態變量保證惟一(這樣只是單線程模式下適用)。外部經過Singleton.GetInstance()獲取惟一對象實例。後端
class Singleton
{
private static Singleton instance;
private Singleton()
{
}
public static Singleton GetSingleton()
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
套路2
:多線程下簡單粗暴的方式就是lock加鎖,讓代碼塊只能有一個線程進入。設計模式
private static readonly object syncRoot = new object();
public static Singleton GetSingleton()
{
lock (syncRoot)
{
if (instance == null)
{
instance = new Singleton();
}
}
return instance;
}
再優化一下,上面這種方法會致使多個線程都須要等待,不管實例是否已經建立。咱們想在實例已經建立的狀況下多線程就不須要等待,直接返回就行。在lock外面加個判斷null能夠保證之後的多線程訪問不用排隊等待。這就是雙重鎖定。
public static Singleton GetSingleton()
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
套路3
:上面的已經徹底實現了單例模式,但還有一個更簡單的方式-靜態初始化。CLR會在類加載時初始化靜態字段,且是線程安全的,因此能夠把類實例化放在靜態字段上。
public sealed class Singleton
{
private static readonly Singleton instance = new Singleton();
private Singleton()
{
}
public static Singleton GetSingleton()
{
return instance;
}
}
這種稱之爲餓漢式單例,類一加載就實例化對象。前面的是懶漢式,在第一次引用時實例化。
簡單工廠模式:什麼是工廠?現實中就是生產東西的地方,程序裏也是,就是有一個單獨的地方(類)來負責建立各類實例
。
以經典的加減乘除計算程序爲例,若是按照面向過程流程開發,大概步驟就是用戶輸入數字1,而後輸入運算符,輸入數字2,而後根據運算符if或switch判斷調用哪一個數學方法,這種沒有面向對象,都在一塊兒,耦合太緊(代碼就不貼了,都是過來人)
先看一下使用簡單工廠模式以後的類圖
把加減乘除各個運算操做封裝成單獨類,都繼承自運算類,共用基類的成員變量AB,重寫GetResult獲取運算結果方法。封裝以後,提供一個簡單工廠類,經過傳入不一樣的操做符,實例化不一樣的操做運算類。這樣增長新的操做運算符時只須要修改工廠就行。(增長一個流水線)
簡單工廠類核心:
public class OperationFactory
{
public static Operation CreateOperate(string operate)
{
Operation oper = null;
switch (operate)
{
case "+":
oper = new OperationAdd();
break;
case "-":
oper = new OperationSub();
break;
case "*":
oper = new OperationMul();
break;
case "/":
oper = new OperationDiv();
break;
}
return oper;
}
}
工廠方法模式:對簡單工廠模式的進一步抽象。簡單工廠是要求把邏輯放在在工廠類中,新增須要修改case分支,違背了開放封閉原則。工廠方法模式進一步在工廠上作文章,定義一個建立對象的接口,讓子類決定實例化哪個類。
工廠部分像下面這樣
interface IFactory
{
Operation CreateOperation() ;
}
class AddFactory : IFactory
{
public Operation CreateOperation()
{
return new OperationAdd();
}
}
class SubFactory : IFactory
{
public Operation CreateOperation()
{
return new OperationSub();
}
}
客戶端調用像下面這樣。若是增長操做運算符,增長相應的運算類和工廠,不須要像簡單工廠那樣修改工廠類內的邏輯。
IFactory operFactory = new AddFactory();
Operation oper = operFactory.CreateOperation();
抽象工廠模式和工廠方法相似。它是提供一個建立一系列對象的工廠接口,無需
指定它們具體的類。咱們看下結構圖
若是說工廠方式模式只是提供單一產品建立接口,那抽象工廠就是讓工廠抽象類擁有建立更多產品的能力,一個汽車生產線包括車架,底盤,輪轂等。抽象工廠的好處便於交換產品系列。如常見的數據庫訪問類。
interface IFactory
{
IUser CreateUser();
IDepartment CreateDepartment();
}
class SqlServerFactory : IFactory
{
public IUser CreateUser()
{
return new SqlserverUser();
}
public IDepartment CreateDepartment()
{
return new SqlserverDepartment();
}
}
class AccessFactory : IFactory
{
public IUser CreateUser()
{
return new AccessUser();
}
public IDepartment CreateDepartment()
{
return new AccessDepartment();
}
}
若是隻是替換產品線比較容易,要是新增一個數據庫訪問表就要修改IFactory,SqlServerFactory ,AccessFactory。 這裏能夠用簡單工廠改進。雖然簡單工廠會多一些switch或if判斷,但能夠經過反射
配置去掉。
又叫生成器模式,將一個複雜產品的生成過程和它的表示分離
,這樣給不一樣的表示就能夠建立出不一樣的產品,就像去買咖啡,加不加糖,加幾塊,加不加奶,作出來就是不一樣的咖啡,用戶只須要指定我要冰美式就行。
有多個產品Builder構建類生成不一樣的產品,用戶Director指揮者指定一種產品就能夠經過GetResult獲取這款產品。這個比較好理解,產品建立過程內部完整高內聚,只對外暴露產品需求,須要什麼產品,內部建立後給客戶。
原型模式用於建立重複的對象而不須要知道建立的細節。通常在初始化的信息不發生變化時,克隆Copy能夠動態的獲取一個運行時的對象,並且效率相比構造函數會提升。原型模式克隆對象應該是因爲類型本身完成的
。
在dotNET中提供了一個ICloneable接口(代替原型抽象類Prototype的功能)。只須要實現這個接口就能夠完成原型模式。
class MyClass : ICloneable
{
public object Clone()
{
return this.MemberwiseClone();
}
}
注意:MemberwiseClone是淺拷貝,就是對於引用類型只複製了引用,而沒有真的把引用類型堆地址複製一份,值類型倒沒問題是真的內存上覆制一份。因此這樣若是生成一個拷貝類,則修改拷貝類中的引用類型,原類也會跟着變更。所以使用深拷貝老老實實在Clone方法裏調用重載構造函數(直到沒有引用類型成員)初始化拷貝類,而後將值類型變量賦值。
結構型設計模式關注的是對象與對象之間的關係,像拼積木,組合合併給程序提供更好的靈活和擴展
聯想到電源的適配器,各個國家的電壓不同,爲了知足電器使用電壓就須要適配器轉換成額定電壓,那麼適配器模式的定義就是將一個類的接口轉換成客戶但願的另一個接口,使得本來因爲接口不兼容而不能一塊兒工做的那些類能夠一塊兒工做。那麼如何轉換呢,核心就是適配器繼承或依賴已有的對象,實現想要的目標接口
。
核心經過繼承或依賴:
class Adapter : Target
{
private Adaptee adaptee = new Adaptee();
public override void Request()
{
adaptee.SpecificRequest();
}
}
主要解決現有類不能知足系統接口要求下,將現有類(能夠是多個)包裝到接口繼承類下。這樣接口繼承類就能調用接口來實際調用現有類功能。雖然提升類的複用,增長靈活,但過多使用致使內部太透明,明明調用的是A接口,後臺又實際調用的B接口。因此能夠看出適配器是在項目服役時期作的靈活調整,屬於臨陣磨槍,設計期能重構不用最好。不便重構的狀況下,適配器能夠快速統一接口。
人靠衣服馬靠鞍,裝飾模式就像給人搭配服飾,能夠根據不一樣場合搭配不一樣的風格,裝飾模式能夠動態的給一個對象添加額外的功能職責
(比直接用子類靈活一些),在不增長子類的狀況下擴展類。
核心套路
:Component 類充當抽象角色,不具體實現,ConcreteComponent子類表明實際的對象,如今要給這個對象添加一些新的功能。裝飾類繼承而且引用Component類,經過設置裝飾類中的Component屬性調用具體被裝飾對象ConcreateComponent方法。可能有些繞,關鍵在於裝飾類和具體被裝飾對象都要繼承相同的抽象Component 類。
看下代碼也許會更容易理解
abstract class Component
{
public abstract void Operation();
}
class ConcreteComponent : Component
{
public override void Operation()
{
Console.WriteLine("具體被裝飾對象的操做 ")
}
}
abstract class Decorator : Component
{
protected Component component;
public void SetComponent(Component component)
{
this.component = component;
}
public override void Operation()
{
if (component != null)
{
component.Operation();
}
}
}
class ConcreateDecoratorA : Decorator
{
private string addedState;
public override void Operation()
{
base.Operation();//執行原Component的功能
//能夠添加須要裝飾的東西,增長什麼功能
addedState = "New State";
Console.WriteLine("具體裝飾對象A的操做 ");
}
}
class ConcreateDecoratorB : Decorator
{
public override void Operation()
{
base.Operation();//執行原Component的功能
//能夠添加須要裝飾的東西,增長什麼功能
AddedBehavior();
Console.WriteLine("具體裝飾對象B的操做 ");
}
private void AddedBehavior()
{
}
}
客戶端調用時像下面這樣。
ConcreteComponent c = new ConcreteComponent();
ConcreateDecoratorA d1 = new ConcreateDecoratorA();
ConcreateDecoratorB d2 = new ConcreateDecoratorB();
d1.SetComponent(c);
d2.SetComponent(d1);
d2.Operation();
利用SetComponent將待裝飾對象包裝到裝飾器中,而後調用裝飾器的同名方法(由於都繼承相同的抽象類)。像上面這樣能夠定義不一樣的裝飾器,會按順序逐個調用裝飾的部分。
總之,裝飾模式能夠動態擴展
一個實現類的功能,當有實現類須要增長特殊行爲時,能夠用一個單獨的裝飾類去實現,(把被裝飾類也就是實現類包裝進去便可),像前面的也能夠不用一個Component抽象類,直接用裝飾類繼承被裝飾類就不須要修改原類。有一個很差的地方就是若是裝飾行爲多了,請注意裝飾順序。
像路由器橋接同樣,將二者解耦,經過一個橋接接口連通。橋接模式用於把抽象部分和實現部分分離解耦
,使得二者能夠獨立變化。
橋接模式中一個重要的概念就是用關聯組合代替繼承,從而下降類與類之間的耦合和臃腫。好比一個繪製圖形的功能,有圓形、正方形等不一樣的圖形,它們還有不一樣的顏色。用繼承關係設計可能像下面這樣。能夠看見類比較多
還有一種方案就是對圖形和顏色進行組合獲得想要的顏色圖形。
因此對於有多個維度變化的系統,採用第二種方案系統中的類個數會更小,擴展方便。
abstract class Implementor
{
public abstract void Operation();
}
class ConcreteImplementorA : Implementor
{
public override void Operation()
{
Console.WriteLine("具體實現A的方法執行");
}
}
class ConcreteImplementorB : Implementor
{
public override void Operation()
{
Console.WriteLine("具體實現B的方法執行");
}
}
class Abstraction
{
protected Implementor implementor;
public void SetImplementor(Implementor implementor)
this.implementor = implementor;
}
public virtual void Operation()
{
implementor.Operation();
}
}
class RefinedAbstraction : Abstraction
{
public override void Operation()
{
implementor.Operation();
}
}
客戶端調用
Abstraction ab = new RefinedAbstraction();
ab.SetImplementor(new ConcreteImplementorA());
ab.Operation();
ab.SetImplementor(new ConcreteImplementorB());
ab.Operation();
若是系統可能有多個角度的分類,每一種角度均可能變化,就能夠不用靜態的繼承鏈接,經過橋接模式可使它們在抽象層創建關聯關係。
組合模式也就是部分整理關係
,將對象組合成樹形結構以表示「部分總體」的層次結構,組合模式使得用戶對單個對象和組合對象的使用具備一致性,用於把一組類似的對象當作一個單一的對象。
核心
就是讓樹枝和葉子實現統一的接口,樹枝內部組合該接口,而且含有內部屬性List放Component。
abstract class Component
{
protected string name;
public Component(string name)
{
this.name = name;
}
public abstract void Add(Component c);
public abstract void Remove(Component c);
public abstract void Display(int depth);
}
class Leaf : Component
{
public Leaf(string name) : base(name)
{ }
public override void Add(Component c)
{
Console.WriteLine("Cannot add to a leaf");
}
public override void Remove(Component c)
{
Console.WriteLine("Cannot remove to a leaf");
}
public override void Display(int depth)
{
Console.WriteLine(new string('-', depth) + name);
}
}
class Composite : Component
{
private List<Component> children = new List<Component> { };
public Composite(string name) : base(name)
{ }
public override void Add(Component c)
{
children.Add(c);
}
public override void Remove(Component c)
{
children.Remove(c);
}
public override void Display(int depth)
{
Console.WriteLine(new string('-', depth) + name);
foreach (Component component in children)
{
component.Display(depth + 2);
}
}
}
客戶端使用像下面這樣
Composite root = new Composite("root");
root.Add(new Leaf("Leaf A"));
root.Add(new Leaf("Leaf B"));
Composite comp = new Composite("Composite X");
comp.Add(new Leaf("Leaf XA"));
comp.Add(new Leaf("Leaf XB"));
root.Add(comp);
Composite comp2 = new Composite("Composite XY");
comp2.Add(new Leaf("Leaf XYA"));
comp2.Add(new Leaf("Leaf XYB"));
comp.Add(comp2);
root.Add(new Leaf("Leaf C"));
root.Display(1);
或者像公司體系結構這種當需求中體現部分總體層次結構,以及你但願用戶能夠忽略組合對象與單個對象的不一樣,統一地使用組合結構中的全部對象時,考慮使用組合模式。上面例子中客戶端無需關心處理一個葉節點仍是枝節點組合組件,都用統一方式訪問。
享元模式主要用於減小對象的數量,以減小內存佔用和提供性能。享元模式嘗試重用現有的同類對象
,若是未找到匹配的對象,則建立新對象。像棋盤中的棋子,不可能建立一整個全部的棋子對象。因此它主要解決當須要大量的對象,有可能對內存形成溢出,咱們能夠把共同的部分抽象出來,這樣若是有相同的業務請求,直接返回內存中已有的對象,不去重複建立了。
核心
就是用一個字典存儲這些須要重複使用的對象。
經過上面結構圖能夠看見就是把Flyweight享元類聚合到FlyweightFactory享元工廠中,享元工廠中用一個Hashtable存儲這些具體的享元類。
class FlyweightFactory
{
private Hashtable flyweights = new Hashtable();
public FlyweightFactory()
{
flyweights.Add("X", new ConcreateFlyweight);
flyweights.Add("Y", new ConcreateFlyweight);
}
public Flyweight GetFlyweight(string key)
{
return (Flyweight)flyweights[key];
}
}
看上面代碼,估計大都用過,設計模式好多都是這種普普統統,數據庫鏈接池就是使用享元模式。總結下就是在系統中有大量類似對象或者須要緩衝池的場景,類似對象能夠分離出內部和外部狀態,內部狀態就是固有的,不變的。外部狀態能夠經過外部調用傳遞進去。
像代購同樣,咱們拜託他人幫咱們買某個東西。代理模式就是用一個類表明另外一個類的功能
。一般在不方便直接訪問原始對象功能,或者須要對原始對象功能增長一些權限或其餘控制時使用代理模式。
核心
就是增長代理層,讓代理類和真實類都實現相同的接口(或抽象類),而後把真實類關聯到代理類中。
上述結構圖中的核心代碼以下
class Proxy : Subject
{
RealSubject realSubject;
public override void Request()
{
if (realSubject == null)
{
realSubject = new RealSubject();
}
realSubject.Request();
}
}
代理模式其實就是在訪問對象時引入必定程度的間接性,所以能夠附加多種用途。
外觀模式隱藏系統的複雜性,向客戶端提供一個高層訪問系統接口
。這樣下降訪問複雜系統的內部子系統時的複雜度,簡化客戶端交互,就像公司前臺。
核心
就是將複雜系統裏的類關聯到外觀類上。上面結構圖就很清晰,經過外觀類方法調用各個複雜系統類。
這種方式對老系統尤爲有用,新增功能須要用到舊類,若是怕改壞了,就能夠簡單實用外觀封裝。還有就是設計時經典的三層架構也是外觀的體現。
行爲型設計模式關注對象和行爲的分離。行爲型比較多,由於程序邏輯都須要行爲來觸發。
解釋器模式,給定一個語言,定義它的文法的一種表示,並定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。有點拗口,簡單理解就是對於一些固定文法構建一個解釋句子的解釋器
。像正則表達式就是這樣,查詢匹配字符問題發生的頻率很高,就把該問題各個實例表述爲一個簡單語言中的句子,解釋句子來解決問題。比例諜戰片中的加密電報,構建一個解釋器對每一個文法解析。
核心
就是構建語法數,定義終結符與非終結符。實際利用的場景仍是比較少的,並且文法太複雜也很難維護。
實際客戶端調用時像下面這樣遍歷解釋器文法
Context context = new Context();
List<AbstractExpression> list = new ArrayList<>();
list.Add(new TerminalExpression());
list.Add(new NonterminalExpression());
list.Add(new TerminalExpression());
list.Add(new TerminalExpression());
foreach (AbstractExpression abstractExpression in list)
{
abstractExpression.Interpret(context);
}
在模板方法中,一個抽象類公開定義一個算法的執行方式模板,而將一些步驟延遲到子類中
,子類能夠按須要重寫方法實現,但調用統一使用抽象類中定義的方式調用。
上面結構圖中AbstractClass就是一個抽象類(抽象模板),定義並實現了一個模板方法。像下面這樣
看見核心
就是將通用方法封裝在超類中,因此在當子類方法中有一些不變的和可變的行爲,能夠將不變的行爲經過模板方法搬到父類,這樣子類就不須要重複這些不變的部分。是否是很簡單,設計模式就是對面向對象編程,封裝繼承多態的靈活使用。
像公司內的請假流程,若是請很長時間,可能先有部門經理審批,部門經理說時間太長了,須要問下總經理。爲請求建立了一個接受者對象的鏈,讓請求者和接收者解耦。這種模式中,一般每一個接收者都包含對另外一個接收者的引用
,這樣若是這個接收者對象不能處理該請求就傳遞給下一個接收者。
上述結構圖中首先定義一個Handler處理請求抽象類,裏面設置了下一個接收者和處理請求的抽象方法。而後再ConcreateHandler子類中實現具體的請求處理,若是處理不了,就轉發給下一個接收者。
abstract class Handler
{
protected Handler successor;
public void SetSuccessor(Handler successor)
{
this.successor = successor;
}
public abstract void HandleRequest(int request);
}
class ConcreateHandler1 : Handler
{
public override void HandleRequest(int request)
{
if (request >= 0 && request < 10)
{
Console.WriteLine($"{this.GetType().Name}處理請求 {request}");
}
else if (successor != null)
{
successor.HandleRequest(request);
}
}
}
核心就是攔截類都實現統一接口
。Handler裏聚合它本身,在HandlerRequest裏判斷是否能處理,若是不能就向下傳遞,要向誰傳遞就Set進去。因此這種方式是不肯定哪一個接收者會處理請求,一般在攔截器中使用,須要對消息的處理過濾不少道時。像擊鼓傳花。
服務員,再炒個方便麪,10個腰子,服務員就記下來交給炒菜師傅和烤串師傅,這就是命令模式。請求以命名的形式包裹在對象中
,並傳給調用對象。調用對象尋找能夠處理該命名的合適的對象,並把該命名傳給相應的對象執行命名。而且支持可撤銷操做,若是腰子好久沒上來,能夠通知服務員不要了。
核心
就是定義三個角色:一、received真正的命令執行對象二、Command三、invoker使用命令對象的入口。最最終實現Invoker對象經過調用ExecuteCommand方法來調用具體命令Command的Excute方法,Excute方法裏調用實際Received接收者動做。
abstract class Command
{
protected Receiver receiver;
public Command(Receiver receiver)
{
this.receiver = receiver;
}
abstract public void Execute();
}
class ConcreteCommand : Command
{
public ConcreteCommand(Receiver receiver) : base(receiver)
{ }
public override void Execute()
{
receiver.Action();
}
}
class Invoker
{
private Command command;
public void SetCommand(Command command)
{
this.command = command;
}
public void ExecuteCommand()
{
command.Execute();
}
}
class Receiver
{
public void Action()
{
Console.WriteLine("執行請求!");
}
}
調用時像下面這樣
Receiver r = new Receiver();
Command c = new ConcreteCommand(r);
Invoker i = new Invoker();
i.SetCommand(c);
i.ExecuteCommand();
命令模式能夠比較容易設計成命令隊列,方便記錄日誌,支持撤銷重作
迭代器提供一種方法順序訪問一個聚合對象中各個元素,而又不暴露該對象的內部表示。就是把元素之間的遍歷遊走的責任
交給迭代器,而不是集合對象自己,分離了集合對象的遍歷行爲,這樣不暴露集合內部結構,又可以讓外部訪問集合內部數據。
核心
就是定義一個有Next,CurrentItem等方法的Iterator迭代接口,而後在子類具體迭代器ConcreateIterator類(能夠實現多個迭代方式)中定義一個具體的彙集對象,而後遍歷迭代器的Next方法遍歷彙集對象內部數據。
在dotNET框架中已經準備好了相關接口,只須要去實現去就好。
IEumerator支持對非泛型集合的簡單迭代接口,就和上面Iterator同樣
public interface IEnumerator
{
object? Current { get; }
bool MoveNext();
void Reset();
}
經常使用的foeach遍歷,編譯器也是轉化爲了IEnumerator遍歷。由於太經常使用,高級語言都進行了封裝,本身也就不經常使用了。
中介者模式用一個中介對象來封裝一系列的對象交互
。中介者使各對象不須要顯示地相互引用,從而使其耦合鬆散,並且能夠獨立地改變它們之間的交互。就是提供一箇中介類,處理不一樣類之間的通訊。
面向抽象編程要求咱們類應該依賴於抽象
核心
就是定義中介者關聯不一樣的須要通訊的類,通訊類內部也須要關聯具體中介者。通訊類發送信息時實際已經經過中介者來轉發。
咱們看下核心的具體中介者和通訊者以及客戶端調用實例
中介者模式將原來網狀的結構變成星狀結構,因此中介者可能會很龐大。通常應用在一組對象已定義良好可是複雜的方式進行通訊的場合。
備忘錄模式保存一個對象的某個狀態,以便在適當的時候恢復對象
。不少時候咱們須要記錄一個對象的內部狀態,這樣可讓用戶去恢復。像玩魔塔遊戲存進度同樣。
結構圖中Originator發起人負責建立一個備忘錄,保存備忘錄的內部數據。Memento備忘錄包含要備份的數據,Caretaker管理者獲得或設置備忘錄。
若是保存所有信息,咱們可使用Clone實現,但若是隻保存部分信息,就應該有一個獨立的Memento備忘錄類。
觀察者模式也叫發佈訂閱模式,定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態發生改變時,會通知全部觀察者對象,是它們可以主動更新本身
。這也是很經常使用的,像委託事件就是這種模式
核心
是將全部通知者抽象類中用一個集合放全部觀察者們。當通知者ConcreateSubject發起Notify通知時,逐個調用集合中觀察者進行Update更新。
abstract class Subject
{
private IList<Observer> observers = new List<Observer>();
// 增長觀察者
public void Attach(Observer observer)
{
observers.Add(observer);
}
// 移除觀察者
public void Detach(Observer observer)
{
observers.Remove(observer);
}
// 通知
public void Notify()
{
foreach (Observer o in observers)
{
o.Update();
}
}
}
當一個對象的改變須要通知其餘對象時使用,通常這種通知可能經過異步來處理。
在狀態模式中,類的行爲時基於它的狀態改變的。就是當一個對象的內部狀態發生改變時改變它的行爲
。主要解決當控制一個對象狀態轉換的條件表達式過於複雜時,把判斷的邏輯遷移到表示不一樣狀態的一系列類中,達到給功能類瘦身的目的。
上面結構圖中將狀態單獨從Context功能類中抽出來,先有一個抽象的狀態類,而後具體不一樣的狀態能夠繼承實現不一樣狀態的Handle處理行爲,最後把抽象狀態聚合放到Context中去,最終調用Context的Request會根據不一樣的狀態觸發不一樣的Handle行爲。
看下核心的代碼。也能夠在每個狀態子類Handle行爲中設置Context的下一個狀態,下次調用Request就觸發相應的狀態行爲。
核心
就是將狀態和行爲放入一個對象中。這麼多種設計模式都有不少相像的地方,反正就是面向對象,分分合合,像先後端同樣,各有優劣。這裏就和命令模式處理的問題很像,均可以用做if分支語句的代替。經過狀態模式能夠很容易的增長新的狀態,把狀態行爲封裝起來,減輕了功能類。
策略模式定義一個類的行爲算法能夠在運行時更改, 把這些算法一個個封裝起來
,並使它們能夠相互替換。
和前面狀態模式結構圖無差異,就是用策略代替了狀態,描述不一樣的問題,解決方法同樣。硬要找個不一樣,大概就是在Context中,狀態模式調用時會傳遞自己引用到各個子狀態的以實現狀態的改變,策略模式中不須要傳遞,只在初始化時指定策略。
核心
和前面同樣,不一樣策略實現同一個接口,聚合到Context類中。也是爲了不多重判斷,擴展性好,能夠隨意切換新增算法。像商場裏的打折促銷滿減不一樣活動,可能會有人想到,不一樣策略的選擇仍是須要判斷語句,能夠結合簡單工廠進一步處理。追求極致那就反射嘍。反射反射,程序員的快樂。
訪問者模式應該是這裏面最複雜的,大多數時候你並不須要使用它。由於訪問者模式表示做用於某對象結構中各元素的操做,它使你在不改變元素的類的前提下定義新操做,而對象數據結構是在不變
的狀況下。
不要怕這個結構圖,一共就兩個部分,首先提供訪問者Visitor類,它的子類就是具體的對元素的操做算法,而後ObjectStructure就是元素集合類,裏面遍歷每一個元素執行元素相對應的算法。因此關鍵就在下面部分Element類中將Visitor做爲輸入參數。
核心
就是在被訪問者的類中加一個對外提供接待訪問者的接口,也就是在數據元素類中有一個方法接收訪問者,將自身引用傳入訪問者,像下面示例這樣
class ConcreateElementA : Element
{
public override void Accept(Visitor visitor)
{
visitor.VisitorConcreateElementA(this);
}
}
class ObjectStructure
{
private List<Element> elements = new List<Element>();
public void Attach(Element element)
{
elements.Add(element);
}
public void Detach(Element element)
{
elements.Remove(element);
}
public void Accept(Visitor visitor)
{
foreach (Element e in elements)
{
e.Accept(visitor);
}
}
}
在對象結構不多變更,須要在此對象結構上定義新的操做或者自己它就有不少不相關的操做時能夠考慮使用此模式。
設計模式可能對於小白來講高大上,其實你實際也常常會使用到,只是不知道那就是設計模式,「優秀」老是那麼類似
。
不用刻意去追求設計模式,一個問題也可能有不少解決方案,往良好的設計去優化。本身用的多了,就知道什麼場景使用什麼設計,最終會與大神不謀而合的。
主要參考程傑的《大話設計模式》
拜了拜