本文主要討論行爲型模式正則表達式
職責鏈模式(Chain of Responsibility Pattern):避免請求發送者與接收者耦合在一塊兒,讓多個對象都有可能接收請求,將這些對象鏈接成一條鏈,而且沿着這條鏈傳遞請求,直到有對象處理它爲止。職責鏈模式是一種對象行爲型模式。算法
職責鏈模式結構的核心在於引入了一個抽象處理者、職責鏈模式的核心在於抽象處理者類的設計
--------------------- 數據庫
● Handler(抽象處理者):它定義了一個處理請求的接口,通常設計爲抽象類,因爲不一樣的具體處理者處理請求的方式不一樣,所以在其中定義了抽象請求處理方法。由於每個處理者的下家仍是一個處理者,所以在抽象處理者中定義了一個抽象處理者類型的對象(如結構圖中的successor),做爲其對下家的引用。經過該引用,處理者能夠連成一條鏈。編程
● ConcreteHandler(具體處理者):它是抽象處理者的子類,能夠處理用戶請求,在具體處理者類中實現了抽象處理者中定義的抽象請求處理方法,在處理請求以前須要進行判斷,看是否有相應的處理權限,若是能夠處理請求就處理它,不然將請求轉發給後繼者;在具體處理者中能夠訪問鏈中下一個對象,以便請求的轉發。設計模式
在職責鏈模式裏,不少對象由每個對象對其下家的引用而鏈接起來造成一條鏈。請求在這個鏈上傳遞,直到鏈上的某一個對象決定處理此請求。發出這個請求的客戶端並不知道鏈上的哪個對象最終處理這個請求,這使得系統能夠在不影響客戶端的狀況下動態地從新組織鏈和分配責任。
--------------------- 瀏覽器
ConcreteHandlerA ConcreteHandlerB的繼承能夠看作一個,加上successor的聚合引用關係,就兩個 關係,因此通常來講, 職責鏈模式是很容易理解和使用的。安全
具體處理者是抽象處理者的子類,它具備兩大做用:第一是處理請求,不一樣的具體處理者以不一樣的形式實現抽象請求處理方法handleRequest();第二是轉發請求,若是該請求超出了當前處理者類的權限,能夠將該請求轉發給下家。—— 須要檢查 權限是否足夠。數據結構
須要注意的是,職責鏈模式並不建立職責鏈,職責鏈的建立工做必須由系統的其餘部分來完成,通常是在使用該職責鏈的客戶端中建立職責鏈。職責鏈模式下降了請求的發送端和接收端之間的耦合,使多個對象都有機會處理這個請求。框架
(1) 純的職責鏈模式編程語言
一個純的職責鏈模式要求一個具體處理者對象只能在兩個行爲中選擇一個:要麼承擔所有責任,要麼將責任推給下家,不容許出現某一個具體處理者對象在承擔了一部分或所有責任後又將責任向下傳遞的狀況。並且在純的職責鏈模式中,要求一個請求必須被某一個處理者對象所接收,不能出現某個請求未被任何一個處理者對象處理的狀況。在前面的採購單審批實例中應用的是純的職責鏈模式。
(2)不純的職責鏈模式
在一個不純的職責鏈模式中容許某個請求被一個具體處理者部分處理後再向下傳遞,或者一個具體處理者處理完某請求後其後繼處理者能夠繼續處理該請求,並且一個請求能夠最終不被任何處理者對象所接收。Java AWT 1.0中的事件處理模型應用的是不純的職責鏈模式,其基本原理以下:因爲窗口組件(如按鈕、文本框等)通常都位於容器組件中,所以當事件發生在某一個組件上時,先經過組件對象的handleEvent()方法將事件傳遞給相應的事件處理方法,該事件處理方法將處理此事件,而後決定是否將該事件向上一級容器組件傳播;上級容器組件在接到事件以後能夠繼續處理此事件並決定是否繼續向上級容器組件傳播,如此反覆,直到事件到達頂層容器組件爲止;若是一直傳到最頂層容器仍沒有處理方法,則該事件不予處理。每一級組件在接收到事件時,均可以處理此事件,而不論此事件是否在上一級已獲得處理,還存在事件未被處理的狀況。顯然,這就是不純的職責鏈模式,早期的Java AWT事件模型(JDK 1.0及更早)中的這種事件處理機制又叫事件浮升(Event Bubbling)機制。從Java.1.1之後,JDK使用觀察者模式代替職責鏈模式來處理事件。目前,在JavaScript中仍然可使用這種事件浮升機制來進行事件處理。
---------------------
職責鏈模式的主要優勢以下:
(1) 職責鏈模式使得一個對象無須知道是其餘哪個對象處理其請求,對象僅需知道該請求會被處理便可,接收者和發送者都沒有對方的明確信息,且鏈中的對象不須要知道鏈的結構,由客戶端負責鏈的建立,下降了系統的耦合度。
(2) 請求處理對象僅需維持一個指向其後繼者的引用,而不須要維持它對全部的候選處理者的引用,可簡化對象的相互鏈接。
(3) 在給對象分派職責時,職責鏈能夠給咱們更多的靈活性,能夠經過在運行時對該鏈進行動態的增長或修改來增長或改變處理一個請求的職責。
(4) 在系統中增長一個新的具體請求處理者時無須修改原有系統的代碼,只須要在客戶端從新建鏈便可,從這一點來看是符合「開閉原則」的。
2.主要缺點
職責鏈模式的主要缺點以下:
(1) 因爲一個請求沒有明確的接收者,那麼就不能保證它必定會被處理,該請求可能一直到鏈的末端都得不處處理;一個請求也可能因職責鏈沒有被正確配置而得不處處理。
(2) 對於比較長的職責鏈,請求的處理可能涉及到多個處理對象,系統性能將受到必定影響,並且在進行代碼調試時不太方便。
(3) 若是建鏈不當,可能會形成循環調用,將致使系統陷入死循環。
---------------------
3.適用場景
在如下狀況下能夠考慮使用職責鏈模式:
(1) 有多個對象能夠處理同一個請求,具體哪一個對象處理該請求待運行時刻再肯定,客戶端只需將請求提交到鏈上,而無須關心請求的處理對象是誰以及它是如何處理的。
(2) 在不明確指定接收者的狀況下,向多個對象中的一個提交一個請求。
(3) 可動態指定一組對象處理請求,客戶端能夠動態建立職責鏈來處理請求,還能夠改變鏈中處理者之間的前後次序。
---------------------
示例: 攔截器、過濾器、請假、報銷審批(須要按額度 層層判斷的那種)
拓展性:能夠靈活的增長一個Handler。 由客戶端能夠動態建立職責鏈來處理請求。
簡化: 只有一個繼承關係+ 聚合關係,顯然,都是不可或缺,於是沒法再繼續簡化了
討論
主要解決了 多重判斷 的問題。就像跟女人同樣,咱們都喜歡純的,可是實際遇到不純的,也很多,實屬正常,不可強求必定要「純的」。
Handler 是否能夠有多個handlerRequest方法,多個successor? 顯然是不能夠的, 不然 問題就複雜了。
命令模式(Command Pattern):將一個請求封裝爲一個對象,從而讓咱們可用不一樣的請求對客戶進行參數化;對請求排隊或者記錄請求日誌,以及支持可撤銷的操做。命令模式是一種對象行爲型模式,其別名爲動做(Action)模式或事務(Transaction)模式。
命令模式的定義比較複雜,提到了不少術語,例如「用不一樣的請求對客戶進行參數化」、「對請求排隊」,「記錄請求日誌」、「支持可撤銷操做」等,在後面咱們將對這些術語進行一一講解。
命令模式的核心在於引入了命令類,經過命令類來下降發送者和接收者的耦合度,請求發送者只需指定一個命令對象,再經過命令對象來調用請求接收者的處理方法
---------------------
● Command(抽象命令類):抽象命令類通常是一個抽象類或接口,在其中聲明瞭用於執行請求的execute()等方法,經過這些方法能夠調用請求接收者的相關操做。
● ConcreteCommand(具體命令類):具體命令類是抽象命令類的子類,實現了在抽象命令類中聲明的方法,它對應具體的接收者對象,將接收者對象的動做綁定其中。在實現execute()方法時,將調用接收者對象的相關操做(Action)。
● Invoker(調用者):調用者即請求發送者,它經過命令對象來執行請求。一個調用者並不須要在設計時肯定其接收者,所以它只與抽象命令類之間存在關聯關係。在程序運行時能夠將一個具體命令對象注入其中,再調用具體命令對象的execute()方法,從而實現間接調用請求接收者的相關操做。
● Receiver(接收者):接收者執行與請求相關的操做,它具體實現對請求的業務處理。
---------------------
命令模式能夠將請求發送者和接收者徹底解耦,發送者與接收者之間沒有直接引用關係,發送請求的對象只須要知道如何發送請求,而沒必要知道如何完成請求
命令模式的本質是對請求進行封裝,一個請求對應於一個命令,將發出命令的責任和執行命令的責任分割開。每個命令都是一個操做:請求的一方發出請求要求執行一個操做;接收的一方收到請求,並執行相應的操做。命令模式容許請求的一方和接收的一方獨立開來,使得請求的一方沒必要知道接收請求的一方的接口,更沒必要知道請求如何被接收、操做是否被執行、什麼時候被執行,以及是怎麼被執行的。
命令模式的關鍵在於引入了抽象命令類,請求發送者針對抽象命令類編程,只有實現了抽象命令類的具體命令才與請求接收者相關聯。在最簡單的抽象命令類中只包含了一個抽象的execute()方法,每一個具體命令類將一個Receiver類型的對象做爲一個實例變量進行存儲,從而具體指定一個請求的接收者,不一樣的具體命令類提供了execute()方法的不一樣實現,並調用不一樣接收者的請求處理方法。
---------------------
命令模式是一種使用頻率很是高的設計模式,它能夠將請求發送者與接收者解耦,請求發送者經過命令對象來間接引用請求接收者,使得系統具備更好的靈活性和可擴展性。在基於GUI的軟件開發,不管是在電腦桌面應用仍是在移動應用中,命令模式都獲得了普遍的應用。
1. 主要優勢
命令模式的主要優勢以下:
(1) 下降系統的耦合度。因爲請求者與接收者之間不存在直接引用,所以請求者與接收者之間實現徹底解耦,相同的請求者能夠對應不一樣的接收者,一樣,相同的接收者也能夠供不一樣的請求者使用,二者之間具備良好的獨立性。
(2) 新的命令能夠很容易地加入到系統中。因爲增長新的具體命令類不會影響到其餘類,所以增長新的具體命令類很容易,無須修改原有系統源代碼,甚至客戶類代碼,知足「開閉原則」的要求。
(3) 能夠比較容易地設計一個命令隊列或宏命令(組合命令)。
(4) 爲請求的撤銷(Undo)和恢復(Redo)操做提供了一種設計和實現方案。
2. 主要缺點
命令模式的主要缺點以下:
使用命令模式可能會致使某些系統有過多的具體命令類。由於針對每個對請求接收者的調用操做都須要設計一個具體命令類,所以在某些系統中可能須要提供大量的具體命令類,這將影響命令模式的使用。
3. 適用場景
在如下狀況下能夠考慮使用命令模式:
(1) 系統須要將請求調用者和請求接收者解耦,使得調用者和接收者不直接交互。請求調用者無須知道接收者的存在,也無須知道接收者是誰,接收者也無須關心什麼時候被調用。
(2) 系統須要在不一樣的時間指定請求、將請求排隊和執行請求。一個命令對象和請求的初始調用者能夠有不一樣的生命期,換言之,最初的請求發出者可能已經不在了,而命令對象自己仍然是活動的,能夠經過該命令對象去調用請求接收者,而無須關心請求調用者的存在性,能夠經過請求日誌文件等機制來具體實現。
(3) 系統須要支持命令的撤銷(Undo)操做和恢復(Redo)操做。
(4) 系統須要將一組操做組合在一塊兒造成宏命令。
---------------------
上面的UML圖其實畫得不太正確, 它並不能幫助咱們理解命令模式的精髓。
咱們看到,總共有: 一個繼承關係+ 聚合關係+關聯關係。須要特別注意的是, Receiver 應該是一個具體的 ConcreteReceiver , 它和 ConcreteCommand 一 一對應,這樣,咱們纔會明白, 原來是這樣 解耦的啊!! 所謂的解耦, 無非就是「多了一箇中間層」 ,也就是多了command 層。Invoker 實際須要的是Receiver 的工做,可是如今多了 command ,如今Invoker 根本不須要的是Receiver 了,command 能夠隨意的切換Receiver , 從而 解耦了!
討論:
一個請求發送者可否對應多個請求接收者?如何實現? ___ 一個請求發送者能夠對應多個請求接受者,將單個Command的引用改成List,而後在執行時遍歷調用。
—— 命令隊列,「批處理」
Receiver 是否認義成一個接口,·從而讓ConcreteCommand 依賴接口,從而能夠靈活的替換 ConcreteReceiver 呢? 能夠這樣,可是不是必須的。
Command 隨意的切換Receiver , 是否會破壞 開閉原則?是的, ConcreteCommand 定下來的那一刻,那麼其對應的ConcreteReceiver 的應該已經肯定了的,因此不能隨意切換,儘管如此, 可是 Invoker 這一層是不須要變更的! 若是新增 Command ,Invoker也是不須要變更的!
Invoker 和 Command 的關係是 1v1 仍是1 v N ? 徹底能夠是1vN ( 命令隊列 ),client 去使用ConcreteCommand 便可。
Invoker 只要一個實例嗎?no,不一樣 Invoker 擁有不一樣Command 便可。
Invoker 能夠有多個 方法嗎?能夠的,好比一個計算器,有加減乘除等操做,每一個操做要求可以取消,即有 compute,undo 方法,那麼各個具體的計算操做分別實現二者便可。
簡化: 一個繼承關係+ 聚合關係+關聯關係,沒法再簡化了
示例:Struts2 的ActionServlet,Invoker 只有一個,實際的Action爲command ,是很是多的,ConcreteCommand類 調用Receiver(對應?) 完成具體工做, 按需增長。
拓展性:新的命令能夠很容易地加入到系統中。
控制面板、等等
解釋器模式(Interpreter Pattern):定義一個語言的文法,而且創建一個解釋器來解釋該語言中的句子,這裏的「語言」是指使用規定格式和語法的代碼。解釋器模式是一種類行爲型模式。
因爲表達式可分爲終結符表達式和非終結符表達式,所以解釋器模式的結構與組合模式的結構有些相似,但在解釋器模式中包含更多的組成元素
---------------------
● AbstractExpression(抽象表達式):在抽象表達式中聲明瞭抽象的解釋操做,它是全部終結符表達式和非終結符表達式的公共父類。
● TerminalExpression(終結符表達式):終結符表達式是抽象表達式的子類,它實現了與文法中的終結符相關聯的解釋操做,在句子中的每個終結符都是該類的一個實例。一般在一個解釋器模式中只有少數幾個終結符表達式類,它們的實例能夠經過非終結符表達式組成較爲複雜的句子。
● NonterminalExpression(非終結符表達式):非終結符表達式也是抽象表達式的子類,它實現了文法中非終結符的解釋操做,因爲在非終結符表達式中能夠包含終結符表達式,也能夠繼續包含非終結符表達式,所以其解釋操做通常經過遞歸的方式來完成。
● Context(環境類):環境類又稱爲上下文類,它用於存儲解釋器以外的一些全局信息,一般它臨時存儲了須要解釋的語句。
當系統無須提供全局公共信息時能夠省略環境類,可根據實際狀況決定是否須要環境類。
---------------------
TerminalExpression 不可再細分,NonterminalExpression 是TerminalExpression 和 其餘NonterminalExpression 的組合。
總結:
解釋器模式爲自定義語言的設計和實現提供了一種解決方案,它用於定義一組文法規則並經過這組文法規則來解釋語言中的句子。雖然解釋器模式的使用頻率不是特別高,可是它在正則表達式、XML文檔解釋等領域仍是獲得了普遍使用。與解釋器模式相似,目前還誕生了不少基於抽象語法樹的源代碼處理工具,例如Eclipse中的Eclipse AST,它能夠用於表示Java語言的語法結構,用戶能夠經過擴展其功能,建立本身的文法規則。
1. 主要優勢
解釋器模式的主要優勢以下:
(1) 易於改變和擴展文法。因爲在解釋器模式中使用類來表示語言的文法規則,所以能夠經過繼承等機制來改變或擴展文法。
(2) 每一條文法規則均可以表示爲一個類,所以能夠方便地實現一個簡單的語言。
(3) 實現文法較爲容易。在抽象語法樹中每個表達式節點類的實現方式都是類似的,這些類的代碼編寫都不會特別複雜,還能夠經過一些工具自動生成節點類代碼。
(4) 增長新的解釋表達式較爲方便。若是用戶須要增長新的解釋表達式只須要對應增長一個新的終結符表達式或非終結符表達式類,原有表達式類代碼無須修改,符合「開閉原則」。
2. 主要缺點
解釋器模式的主要缺點以下:
(1) 對於複雜文法難以維護。在解釋器模式中,每一條規則至少須要定義一個類,所以若是一個語言包含太多文法規則,類的個數將會急劇增長,致使系統難以管理和維護,此時能夠考慮使用語法分析程序等方式來取代解釋器模式。
(2) 執行效率較低。因爲在解釋器模式中使用了大量的循環和遞歸調用,所以在解釋較爲複雜的句子時其速度很慢,並且代碼的調試過程也比較麻煩。
3. 適用場景
在如下狀況下能夠考慮使用解釋器模式:
(1) 能夠將一個須要解釋執行的語言中的句子表示爲一個抽象語法樹。
(2) 一些重複出現的問題能夠用一種簡單的語言來進行表達。
(3) 一個語言的文法較爲簡單。
(4) 執行效率不是關鍵問題。【注:高效的解釋器一般不是經過直接解釋抽象語法樹來實現的,而是須要將它們轉換成其餘形式,使用解釋器模式的執行效率並不高。】
---------------------
在軟件開發中,咱們常常須要使用聚合對象來存儲一系列數據。聚合對象擁有兩個職責:一是存儲數據;二是遍歷數據。從依賴性來看,前者是聚合對象的基本職責;然後者既是可變化的,又是可分離的。所以,能夠將遍歷數據的行爲從聚合對象中分離出來,封裝在一個被稱之爲「迭代器」的對象中,由迭代器來提供遍歷聚合對象內部數據的行爲,這將簡化聚合對象的設計,更符合「單一職責原則」的要求。
迭代器模式定義以下:
迭代器模式(Iterator Pattern):提供一種方法來訪問聚合對象,而不用暴露這個對象的內部表示,其別名爲遊標(Cursor)。迭代器模式是一種對象行爲型模式。
在迭代器模式結構中包含聚合和迭代器兩個層次結構,考慮到系統的靈活性和可擴展性,在迭代器模式中應用了工廠方法模式
---------------------
理解迭代器模式中具體聚合類與具體迭代器類之間存在的依賴關係和關聯關係。 —— 相對於相關關聯。
主要優勢
迭代器模式的主要優勢以下:
(1) 它支持以不一樣的方式遍歷一個聚合對象,在同一個聚合對象上能夠定義多種遍歷方式。在迭代器模式中只須要用一個不一樣的迭代器來替換原有迭代器便可改變遍歷算法,咱們也能夠本身定義迭代器的子類以支持新的遍歷方式。
(2) 迭代器簡化了聚合類。因爲引入了迭代器,在原有的聚合對象中不須要再自行提供數據遍歷等方法,這樣能夠簡化聚合類的設計。
(3) 在迭代器模式中,因爲引入了抽象層,增長新的聚合類和迭代器類都很方便,無須修改原有代碼,知足「開閉原則」的要求。
2. 主要缺點
迭代器模式的主要缺點以下:
(1) 因爲迭代器模式將存儲數據和遍歷數據的職責分離,增長新的聚合類須要對應增長新的迭代器類,類的個數成對增長,這在必定程度上增長了系統的複雜性。
(2) 抽象迭代器的設計難度較大,須要充分考慮到系統未來的擴展,例如JDK內置迭代器Iterator就沒法實現逆向遍歷,若是須要實現逆向遍歷,只能經過其子類ListIterator等來實現,而ListIterator迭代器沒法用於操做Set類型的聚合對象。在自定義迭代器時,建立一個考慮全面的抽象迭代器並非件很容易的事情。
3. 適用場景
在如下狀況下能夠考慮使用迭代器模式:
(1) 訪問一個聚合對象的內容而無須暴露它的內部表示。將聚合對象的訪問與內部數據的存儲分離,使得訪問聚合對象時無須瞭解其內部實現細節。
(2) 須要爲一個聚合對象提供多種遍歷方式。
(3) 爲遍歷不一樣的聚合結構提供一個統一的接口,在該接口的實現類中爲不一樣的聚合結構提供不一樣的遍歷方式,而客戶端能夠一致性地操做該接口。
---------------------
從:
變成了:
若是在一個系統中對象之間存在多對多的相互關係,咱們能夠將對象之間的一些交互行爲從各個對象中分離出來,並集中封裝在一箇中介者對象中,並由該中介者進行統一協調,這樣對象之間多對多的複雜關係就轉化爲相對簡單的一對多關係。經過引入中介者來簡化對象之間的複雜交互,中介者模式是「迪米特法則」的一個典型應用。
---------------------
中介者模式定義以下:
中介者模式(Mediator Pattern):用一箇中介對象(中介者)來封裝一系列的對象交互,中介者使各對象不須要顯式地相互引用,從而使其耦合鬆散,並且能夠獨立地改變它們之間的交互。中介者模式又稱爲調停者模式,它是一種對象行爲型模式。
在中介者模式中,咱們引入了用於協調其餘對象/類之間相互調用的中介者類,爲了讓系統具備更好的靈活性和可擴展性,一般還提供了抽象中介者
---------------------
中介者模式 感受跟 外觀模式 很像,其實否則,中介者模式 針對的是 系統內部, 相互的調用,雙向的。 外觀模式 關注的是 外部系統對內部各個系統的調用,是單向的。
● Mediator(抽象中介者):它定義一個接口,該接口用於與各同事對象之間進行通訊。
● ConcreteMediator(具體中介者):它是抽象中介者的子類,經過協調各個同事對象來實現協做行爲,它維持了對各個同事對象的引用。
● Colleague(抽象同事類):它定義各個同事類公有的方法,並聲明瞭一些抽象方法來供子類實現,同時它維持了一個對抽象中介者類的引用,其子類能夠經過該引用來與中介者通訊。
● ConcreteColleague(具體同事類):它是抽象同事類的子類;每個同事對象在須要和其餘同事對象通訊時,先與中介者通訊,經過中介者來間接完成與其餘同事類的通訊;在具體同事類中實現了在抽象同事類中聲明的抽象方法。
中介者模式的核心在於中介者類的引入,在中介者模式中,中介者類承擔了兩方面的職責:
(1) 中轉做用(結構性):經過中介者提供的中轉做用,各個同事對象就再也不須要顯式引用其餘同事,當須要和其餘同事進行通訊時,可經過中介者來實現間接調用。該中轉做用屬於中介者在結構上的支持。
(2) 協調做用(行爲性):中介者能夠更進一步的對同事之間的關係進行封裝,同事能夠一致的和中介者進行交互,而不須要指明中介者須要具體怎麼作,中介者根據封裝在自身內部的協調邏輯,對同事的請求進行進一步處理,將同事成員之間的關係行爲進行分離和封裝。該協調做用屬於中介者在行爲上的支持。
---------------------
在具體同事類ConcreteColleague中實現了在抽象同事類中聲明的方法,其中方法method1()是同事類的自身方法(Self-Method),用於處理本身的行爲,而方法method2()是依賴方法(Depend-Method),用於調用在中介者中定義的方法,依賴中介者來完成相應的行爲,例如調用另外一個同事類的相關方法
---------------------
如何理解同事類中的自身方法與依賴方法? 一個有外部依賴Mediator ,一個沒有。
中介者模式將一個網狀的系統結構變成一個以中介者對象爲中心的星形結構,在這個星型結構中,使用中介者對象與其餘對象的一對多關係來取代原有對象之間的多對多關係。中介者模式在事件驅動類軟件中應用較爲普遍,特別是基於GUI(Graphical User Interface,圖形用戶界面)的應用軟件,此外,在類與類之間存在錯綜複雜的關聯關係的系統中,中介者模式都能獲得較好的應用。
1. 主要優勢
中介者模式的主要優勢以下:
(1) 中介者模式簡化了對象之間的交互,它用中介者和同事的一對多交互代替了原來同事之間的多對多交互,一對多關係更容易理解、維護和擴展,將本來難以理解的網狀結構轉換成相對簡單的星型結構。
(2) 中介者模式可將各同事對象解耦。中介者有利於各同事之間的鬆耦合,咱們能夠獨立的改變和複用每個同事和中介者,增長新的中介者和新的同事類都比較方便,更好地符合「開閉原則」。
(3) 能夠減小子類生成,中介者將本來分佈於多個對象間的行爲集中在一塊兒,改變這些行爲只需生成新的中介者子類便可,這使各個同事類可被重用,無須對同事類進行擴展。
2. 主要缺點
中介者模式的主要缺點以下:
在具體中介者類中包含了大量同事之間的交互細節,可能會致使具體中介者類很是複雜,使得系統難以維護。
3. 適用場景
在如下狀況下能夠考慮使用中介者模式:
(1) 系統中對象之間存在複雜的引用關係,系統結構混亂且難以理解。
(2) 一個對象因爲引用了其餘不少對象而且直接和這些對象通訊,致使難以複用該對象。
(3) 想經過一箇中間類來封裝多個類中的行爲,而又不想生成太多的子類。能夠經過引入中介者類來實現,在中介者中定義對象交互的公共行爲,若是須要改變行爲則能夠增長新的具體中介者類。
---------------------
拓展性: 若是須要引入新的具體同事類,只須要繼承抽象同事類並實現其中的方法便可,因爲具體同事類之間並沒有直接的引用關係,所以原有全部同事類無須進行任何修改,它們與新增同事對象之間的交互能夠經過修改或者增長具體中介者類來實現;若是須要在原有系統中增長新的具體中介者類,只須要繼承抽象中介者類(或已有的具體中介者類)並覆蓋其中定義的方法便可,在新的具體中介者中能夠經過不一樣的方式來處理對象之間的交互,也能夠增長對新增同事的引用和調用。在客戶端中只須要修改少量代碼(若是引入配置文件的話有時能夠不修改任何代碼)就能夠實現中介者的更換。
討論:
ConcreteMediator是必須的嗎? 感受若是簡單的話,能夠不用吧~!
Mediator 一般有哪些接口方法? 協調各個 Colleague 的各類方法, 會不會很是多?
ConcreteColleague 若是沒有統一實現Colleague 怎麼辦? 這種狀況 可以經過中介來 協調嗎?
我的感受這個設計模式, 不算是真正的設計模式,不如說成一個 設計思想 更好吧~!
備忘錄模式提供了一種狀態恢復的實現機制,使得用戶能夠方便地回到一個特定的歷史步驟,當新的狀態無效或者存在問題時,可使用暫時存儲起來的備忘錄將狀態復原,當前不少軟件都提供了撤銷(Undo)操做,其中就使用了備忘錄模式。
備忘錄模式定義以下:
備忘錄模式(Memento Pattern):在不破壞封裝的前提下,捕獲一個對象的內部狀態,並在該對象以外保存這個狀態,這樣能夠在之後將對象恢復到原先保存的狀態。它是一種對象行爲型模式,其別名爲Token。
備忘錄模式的核心是備忘錄類以及用於管理備忘錄的負責人類的設計
---------------------
● Originator(原發器):它是一個普通類,能夠建立一個備忘錄,並存儲它的當前內部狀態,也可使用備忘錄來恢復其內部狀態,通常將須要保存內部狀態的類設計爲原發器。
●Memento(備忘錄):存儲原發器的內部狀態,根據原發器來決定保存哪些內部狀態。備忘錄的設計通常能夠參考原發器的設計,根據實際須要肯定備忘錄類中的屬性。須要注意的是,除了原發器自己與負責人類以外,備忘錄對象不能直接供其餘類使用,原發器的設計在不一樣的編程語言中實現機制會有所不一樣。
●Caretaker(負責人):負責人又稱爲管理者,它負責保存備忘錄,可是不能對備忘錄的內容進行操做或檢查。在負責人類中能夠存儲一個或多個備忘錄對象,它只負責存儲對象,而不能修改對象,也無須知道對象的實現細節。
理解備忘錄模式並不難,但關鍵在於如何設計備忘錄類和負責人類。因爲在備忘錄中存儲的是原發器的中間狀態,所以須要防止原發器之外的其餘對象訪問備忘錄,特別是不容許其餘對象來修改備忘錄。
---------------------
在設計備忘錄類時須要考慮其封裝性,除了Originator類,不容許其餘類來調用備忘錄類Memento的構造函數與相關方法,若是不考慮封裝性,容許其餘類調用setState()等方法,將致使在備忘錄中保存的歷史狀態發生改變,經過撤銷操做所恢復的狀態就再也不是真實的歷史狀態,備忘錄模式也就失去了自己的意義。
---------------------
對於負責人類Caretaker,它用於保存備忘錄對象,並提供getMemento()方法用於向客戶端返回一個備忘錄對象,原發器經過使用這個備忘錄對象能夠回到某個歷史狀態
再談備忘錄的封裝
備忘錄是一個很特殊的對象,只有原發器對它擁有控制的權力,負責人只負責管理,而其餘類沒法訪問到備忘錄,所以咱們須要對備忘錄進行封裝。
爲了實現對備忘錄對象的封裝,須要對備忘錄的調用進行控制,對於原發器而言,它能夠調用備忘錄的全部信息,容許原發器訪問返回到先前狀態所需的全部數據;對於負責人而言,只負責備忘錄的保存並將備忘錄傳遞給其餘對象;對於其餘對象而言,只須要從負責人處取出備忘錄對象並將原發器對象的狀態恢復,而無須關心備忘錄的保存細節。理想的狀況是隻容許生成該備忘錄的那個原發器訪問備忘錄的內部狀態。
在實際開發中,原發器與備忘錄之間的關係是很是特殊的,它們要分享信息而不讓其餘類知道,實現的方法因編程語言的不一樣而有所差別,在C++中可使用friend關鍵字,讓原發器類和備忘錄類成爲友元類,互相之間能夠訪問對象的一些私有的屬性;在Java語言中能夠將原發器類和備忘錄類放在一個包中,讓它們之間知足默認的包內可見性,也能夠將備忘錄類做爲原發器類的內部類,使得只有原發器才能夠訪問備忘錄中的數據,其餘對象都沒法使用備忘錄中的數據。
---------------------
備忘錄模式在不少軟件的使用過程當中廣泛存在,可是在應用軟件開發中,它的使用頻率並不過高,由於如今不少基於窗體和瀏覽器的應用軟件並無提供撤銷操做。若是須要爲軟件提供撤銷功能,備忘錄模式無疑是一種很好的解決方案。在一些字處理軟件、圖像編輯軟件、數據庫管理系統等軟件中備忘錄模式都獲得了很好的應用。
1.主要優勢
備忘錄模式的主要優勢以下:
(1)它提供了一種狀態恢復的實現機制,使得用戶能夠方便地回到一個特定的歷史步驟,當新的狀態無效或者存在問題時,可使用暫時存儲起來的備忘錄將狀態復原。
(2)備忘錄實現了對信息的封裝,一個備忘錄對象是一種原發器對象狀態的表示,不會被其餘代碼所改動。備忘錄保存了原發器的狀態,採用列表、堆棧等集合來存儲備忘錄對象能夠實現屢次撤銷操做。
2.主要缺點
備忘錄模式的主要缺點以下:
資源消耗過大,若是須要保存的原發器類的成員變量太多,就不可避免須要佔用大量的存儲空間,每保存一次對象的狀態都須要消耗必定的系統資源。
3.適用場景
在如下狀況下能夠考慮使用備忘錄模式:
(1)保存一個對象在某一個時刻的所有狀態或部分狀態,這樣之後須要時它可以恢復到先前的狀態,實現撤銷操做。
(2)防止外界對象破壞一個對象歷史狀態的封裝性,避免將對象歷史狀態的實現細節暴露給外界對象。
---------------------
討論:
可否經過原型模式來建立備忘錄對象?系統該如何設計? —— 也是能夠吧。
觀察者模式是使用頻率最高的設計模式之一,它用於創建一種對象與對象之間的依賴關係,一個對象發生改變時將自動通知其餘對象,其餘對象將相應做出反應。在觀察者模式中,發生改變的對象稱爲觀察目標,而被通知的對象稱爲觀察者,一個觀察目標能夠對應多個觀察者,並且這些觀察者之間能夠沒有任何相互聯繫,能夠根據須要增長和刪除觀察者,使得系統更易於擴展。
觀察者模式定義以下:
觀察者模式(Observer Pattern):定義對象之間的一種一對多依賴關係,使得每當一個對象狀態發生改變時,其相關依賴對象皆獲得通知並被自動更新。觀察者模式的別名包括髮布-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監聽器(Source/Listener)模式或從屬者(Dependents)模式。觀察者模式是一種對象行爲型模式。
觀察者模式結構中一般包括觀察目標和觀察者兩個繼承層次結構,
---------------------
● Subject(目標):目標又稱爲主題,它是指被觀察的對象。在目標中定義了一個觀察者集合,一個觀察目標能夠接受任意數量的觀察者來觀察,它提供一系列方法來增長和刪除觀察者對象,同時它定義了通知方法notify()。目標類能夠是接口,也能夠是抽象類或具體類。
● ConcreteSubject(具體目標):具體目標是目標類的子類,一般它包含有常常發生改變的數據,當它的狀態發生改變時,向它的各個觀察者發出通知;同時它還實現了在目標類中定義的抽象業務邏輯方法(若是有的話)。若是無須擴展目標類,則具體目標類能夠省略。
● Observer(觀察者):觀察者將對觀察目標的改變作出反應,觀察者通常定義爲接口,該接口聲明瞭更新數據的方法update(),所以又稱爲抽象觀察者。
● ConcreteObserver(具體觀察者):在具體觀察者中維護一個指向具體目標對象的引用,它存儲具體觀察者的有關狀態,這些狀態須要和具體目標的狀態保持一致;它實現了在抽象觀察者Observer中定義的update()方法。一般在實現時,能夠調用具體目標類的attach()方法將本身添加到目標類的集合中或經過detach()方法將本身從目標類的集合中刪除。
觀察者模式描述瞭如何創建對象與對象之間的依賴關係,以及如何構造知足這種需求的系統。觀察者模式包含觀察目標和觀察者兩類對象,一個目標能夠有任意數目的與之相依賴的觀察者,一旦觀察目標的狀態發生改變,全部的觀察者都將獲得通知。做爲對這個通知的響應,每一個觀察者都將監視觀察目標的狀態以使其狀態與目標狀態同步,這種交互也稱爲發佈-訂閱(Publish-Subscribe)。觀察目標是通知的發佈者,它發出通知時並不須要知道誰是它的觀察者,能夠有任意數目的觀察者訂閱它並接收通知。
---------------------
觀察者模式總結
觀察者模式是一種使用頻率很是高的設計模式,不管是移動應用、Web應用或者桌面應用,觀察者模式幾乎無處不在,它爲實現對象之間的聯動提供了一套完整的解決方案,凡是涉及到一對一或者一對多的對象交互場景均可以使用觀察者模式。觀察者模式普遍應用於各類編程語言的GUI事件處理的實現,在基於事件的XML解析技術(如SAX2)以及Web事件處理中也都使用了觀察者模式。
1.主要優勢
觀察者模式的主要優勢以下:
(1) 觀察者模式能夠實現表示層和數據邏輯層的分離,定義了穩定的消息更新傳遞機制,並抽象了更新接口,使得能夠有各類各樣不一樣的表示層充當具體觀察者角色。
(2) 觀察者模式在觀察目標和觀察者之間創建一個抽象的耦合。觀察目標只須要維持一個抽象觀察者的集合,無須瞭解其具體觀察者。因爲觀察目標和觀察者沒有緊密地耦合在一塊兒,所以它們能夠屬於不一樣的抽象化層次。
(3) 觀察者模式支持廣播通訊,觀察目標會向全部已註冊的觀察者對象發送通知,簡化了一對多系統設計的難度。
(4) 觀察者模式知足「開閉原則」的要求,增長新的具體觀察者無須修改原有系統代碼,在具體觀察者與觀察目標之間不存在關聯關係的狀況下,增長新的觀察目標也很方便。
2.主要缺點
觀察者模式的主要缺點以下:
(1) 若是一個觀察目標對象有不少直接和間接觀察者,將全部的觀察者都通知到會花費不少時間。
(2) 若是在觀察者和觀察目標之間存在循環依賴,觀察目標會觸發它們之間進行循環調用,可能致使系統崩潰。
(3) 觀察者模式沒有相應的機制讓觀察者知道所觀察的目標對象是怎麼發生變化的,而僅僅只是知道觀察目標發生了變化。
3.適用場景
在如下狀況下能夠考慮使用觀察者模式:
(1) 一個抽象模型有兩個方面,其中一個方面依賴於另外一個方面,將這兩個方面封裝在獨立的對象中使它們能夠各自獨立地改變和複用。
(2) 一個對象的改變將致使一個或多個其餘對象也發生改變,而並不知道具體有多少對象將發生改變,也不知道這些對象是誰。
(3) 須要在系統中建立一個觸發鏈,A對象的行爲將影響B對象,B對象的行爲將影響C對象……,可使用觀察者模式建立一種鏈式觸發機制。
---------------------
狀態模式用於解決系統中複雜對象的狀態轉換以及不一樣狀態下行爲的封裝問題。當系統中某個對象存在多個狀態,這些狀態之間能夠進行轉換,並且對象在不一樣狀態下行爲不相同時可使用狀態模式。狀態模式將一個對象的狀態從該對象中分離出來,封裝到專門的狀態類中,使得對象狀態能夠靈活變化,對於客戶端而言,無須關心對象狀態的轉換以及對象所處的當前狀態,不管對於何種狀態的對象,客戶端均可以一致處理。
狀態模式定義以下:
狀態模式(State Pattern):容許一個對象在其內部狀態改變時改變它的行爲,對象看起來彷佛修改了它的類。其別名爲狀態對象(Objects for States),狀態模式是一種對象行爲型模式。
在狀態模式中引入了抽象狀態類和具體狀態類,它們是狀態模式的核心
---------------------
● Context(環境類):環境類又稱爲上下文類,它是擁有多種狀態的對象。因爲環境類的狀態存在多樣性且在不一樣狀態下對象的行爲有所不一樣,所以將狀態獨立出去造成單獨的狀態類。在環境類中維護一個抽象狀態類State的實例,這個實例定義當前狀態,在具體實現時,它是一個State子類的對象。
● State(抽象狀態類):它用於定義一個接口以封裝與環境類的一個特定狀態相關的行爲,在抽象狀態類中聲明瞭各類不一樣狀態對應的方法,而在其子類中實現類這些方法,因爲不一樣狀態下對象的行爲可能不一樣,所以在不一樣子類中方法的實現可能存在不一樣,相同的方法能夠寫在抽象狀態類中。
● ConcreteState(具體狀態類):它是抽象狀態類的子類,每個子類實現一個與環境類的一個狀態相關的行爲,每個具體狀態類對應環境的一個具體狀態,不一樣的具體狀態類其行爲有所不一樣。
在狀態模式中,咱們將對象在不一樣狀態下的行爲封裝到不一樣的狀態類中,爲了讓系統具備更好的靈活性和可擴展性,同時對各狀態下的共有行爲進行封裝,咱們須要對狀態進行抽象,引入了抽象狀態類角色
---------------------
在狀態模式的使用過程當中,一個對象的狀態之間還能夠進行相互轉換,一般有兩種實現狀態轉換的方式:
(1) 統一由環境類來負責狀態之間的轉換
(2) 由具體狀態類來負責狀態之間的轉換
狀態模式 的狀態是 根據外部提供 數據, 動態進行變化的, 某條件下是這個狀態,其餘條件是 另外的狀態。 整體而言, 狀態有 Context 驅動。。
狀態模式總結
狀態模式將一個對象在不一樣狀態下的不一樣行爲封裝在一個個狀態類中,經過設置不一樣的狀態對象可讓環境對象擁有不一樣的行爲,而狀態轉換的細節對於客戶端而言是透明的,方便了客戶端的使用。在實際開發中,狀態模式具備較高的使用頻率,在工做流和遊戲開發中狀態模式都獲得了普遍的應用,例如公文狀態的轉換、遊戲中角色的升級等。
1. 主要優勢
狀態模式的主要優勢以下:
(1) 封裝了狀態的轉換規則,在狀態模式中能夠將狀態的轉換代碼封裝在環境類或者具體狀態類中,能夠對狀態轉換代碼進行集中管理,而不是分散在一個個業務方法中。
(2) 將全部與某個狀態有關的行爲放到一個類中,只須要注入一個不一樣的狀態對象便可使環境對象擁有不一樣的行爲。
(3) 容許狀態轉換邏輯與狀態對象合成一體,而不是提供一個巨大的條件語句塊,狀態模式可讓咱們避免使用龐大的條件語句來將業務方法和狀態轉換代碼交織在一塊兒。
(4) 可讓多個環境對象共享一個狀態對象,從而減小系統中對象的個數。
2. 主要缺點
狀態模式的主要缺點以下:
(1) 狀態模式的使用必然會增長系統中類和對象的個數,致使系統運行開銷增大。
(2) 狀態模式的結構與實現都較爲複雜,若是使用不當將致使程序結構和代碼的混亂,增長系統設計的難度。
(3) 狀態模式對「開閉原則」的支持並不太好,增長新的狀態類須要修改那些負責狀態轉換的源代碼,不然沒法轉換到新增狀態;並且修改某個狀態類的行爲也需修改對應類的源代碼。
3. 適用場景
在如下狀況下能夠考慮使用狀態模式:
(1) 對象的行爲依賴於它的狀態(如某些屬性值),狀態的改變將致使行爲的變化。
(2) 在代碼中包含大量與對象狀態有關的條件語句,這些條件語句的出現,會致使代碼的可維護性和靈活性變差,不能方便地增長和刪除狀態,而且致使客戶類與類庫之間的耦合加強。
---------------------
適用於那些 狀態不少,很複雜,行爲隨狀態發生「大變化」 的狀況。
「容許狀態轉換邏輯與狀態對象合成一體,而不是提供一個巨大的條件語句塊」 ———— 然而狀態模式 並不能徹底的消除 if else 條件語句塊, 不過是把「巨大的條件語句塊」 換成了 「比較小的條件語句塊」
在策略模式中,咱們能夠定義一些獨立的類來封裝不一樣的算法,每個類封裝一種具體的算法,在這裏,每個封裝算法的類咱們均可以稱之爲一種策略(Strategy),爲了保證這些策略在使用時具備一致性,通常會提供一個抽象的策略類來作規則的定義,而每種算法則對應於一個具體策略類。
策略模式的主要目的是將算法的定義與使用分開,也就是將算法的行爲和環境分開,將算法的定義放在專門的策略類中,每個策略類封裝了一種實現算法,使用算法的環境類針對抽象策略類進行編程,符合「依賴倒轉原則」。在出現新的算法時,只須要增長一個新的實現了抽象策略類的具體策略類便可。策略模式定義以下:
策略模式(Strategy Pattern):定義一系列算法類,將每個算法封裝起來,並讓它們能夠相互替換,策略模式讓算法獨立於使用它的客戶而變化,也稱爲政策模式(Policy)。策略模式是一種對象行爲型模式。
策略模式結構並不複雜,但咱們須要理解其中環境類Context的做用
---------------------
● Context(環境類):環境類是使用算法的角色,它在解決某個問題(即實現某個方法)時能夠採用多種策略。在環境類中維持一個對抽象策略類的引用實例,用於定義所採用的策略。
● Strategy(抽象策略類):它爲所支持的算法聲明瞭抽象方法,是全部策略類的父類,它能夠是抽象類或具體類,也能夠是接口。環境類經過抽象策略類中聲明的方法在運行時調用具體策略類中實現的算法。
● ConcreteStrategy(具體策略類):它實現了在抽象策略類中聲明的算法,在運行時,具體策略類將覆蓋在環境類中定義的抽象策略類對象,使用一種具體的算法實現某個業務處理。
---------------------
一個環境類Context可否對應多個不一樣的策略等級結構?如何設計? 固然能夠的, 多一個不一樣等級結構 類型的Strategy 引用便可!
這個策略模式和簡單工廠模式具備殊途同歸之妙呀~ 他們都是將if...else這樣難擴展和維護的代碼經過繼承的方式來解決,而後針對抽象類編程,使之擴展的時候知足「開閉原則
策略模式總結
策略模式用於算法的自由切換和擴展,它是應用較爲普遍的設計模式之一。策略模式對應於解決某一問題的一個算法族,容許用戶從該算法族中任選一個算法來解決某一問題,同時能夠方便地更換算法或者增長新的算法。只要涉及到算法的封裝、複用和切換均可以考慮使用策略模式。
1. 主要優勢
策略模式的主要優勢以下:
(1) 策略模式提供了對「開閉原則」的完美支持,用戶能夠在不修改原有系統的基礎上選擇算法或行爲,也能夠靈活地增長新的算法或行爲。
(2) 策略模式提供了管理相關的算法族的辦法。策略類的等級結構定義了一個算法或行爲族,恰當使用繼承能夠把公共的代碼移到抽象策略類中,從而避免重複的代碼。
(3) 策略模式提供了一種能夠替換繼承關係的辦法。若是不使用策略模式,那麼使用算法的環境類就可能會有一些子類,每個子類提供一種不一樣的算法。可是,這樣一來算法的使用就和算法自己混在一塊兒,不符合「單一職責原則」,決定使用哪種算法的邏輯和該算法自己混合在一塊兒,從而不可能再獨立演化;並且使用繼承沒法實現算法或行爲在程序運行時的動態切換。
(4) 使用策略模式能夠避免多重條件選擇語句。多重條件選擇語句不易維護,它把採起哪種算法或行爲的邏輯與算法或行爲自己的實現邏輯混合在一塊兒,將它們所有硬編碼(Hard Coding)在一個龐大的多重條件選擇語句中,比直接繼承環境類的辦法還要原始和落後。
(5) 策略模式提供了一種算法的複用機制,因爲將算法單獨提取出來封裝在策略類中,所以不一樣的環境類能夠方便地複用這些策略類。
2. 主要缺點
策略模式的主要缺點以下:
(1) 客戶端必須知道全部的策略類,並自行決定使用哪個策略類。這就意味着客戶端必須理解這些算法的區別,以便適時選擇恰當的算法。換言之,策略模式只適用於客戶端知道全部的算法或行爲的狀況。
(2) 策略模式將形成系統產生不少具體策略類,任何細小的變化都將致使系統要增長一個新的具體策略類。
(3) 沒法同時在客戶端使用多個策略類,也就是說,在使用策略模式時,客戶端每次只能使用一個策略類,不支持使用一個策略類完成部分功能後再使用另外一個策略類來完成剩餘功能的狀況。
3. 適用場景
在如下狀況下能夠考慮使用策略模式:
(1) 一個系統須要動態地在幾種算法中選擇一種,那麼能夠將這些算法封裝到一個個的具體算法類中,而這些具體算法類都是一個抽象算法類的子類。換言之,這些具體算法類均有統一的接口,根據「里氏代換原則」和麪向對象的多態性,客戶端能夠選擇使用任何一個具體算法類,並只須要維持一個數據類型是抽象算法類的對象。
(2) 一個對象有不少的行爲,若是不用恰當的模式,這些行爲就只好使用多重條件選擇語句來實現。此時,使用策略模式,把這些行爲轉移到相應的具體策略類裏面,就能夠避免使用難以維護的多重條件選擇語句。
(3) 不但願客戶端知道複雜的、與算法相關的數據結構,在具體策略類中封裝算法與相關的數據結構,能夠提升算法的保密性與安全性。
---------------------
模板方法模式定義以下:
模板方法模式:定義一個操做中算法的框架,而將一些步驟延遲到子類中。模板方法模式使得子類能夠不改變一個算法的結構便可重定義該算法的某些特定步驟。
模板方法模式是一種基於繼承的代碼複用技術,它是一種類行爲型模式。
模板方法模式是結構最簡單的行爲型設計模式,在其結構中只存在父類與子類之間的繼承關係。經過使用模板方法模式,能夠將一些複雜流程的實現步驟封裝在一系列基本方法中,在抽象父類中提供一個稱之爲模板方法的方法來定義這些基本方法的執行次序,而經過其子類來覆蓋某些步驟,從而使得相同的算法框架能夠有不一樣的執行結果。模板方法模式提供了一個模板方法來定義算法框架,而某些具體步驟的實現能夠在其子類中完成。
---------------------
模板方法模式結構比較簡單,其核心是抽象類和其中的模板方法的設計
(1) AbstractClass(抽象類):在抽象類中定義了一系列基本操做(PrimitiveOperations),這些基本操做能夠是具體的,也能夠是抽象的,每個基本操做對應算法的一個步驟,在其子類中能夠重定義或實現這些步驟。同時,在抽象類中實現了一個模板方法(Template Method),用於定義一個算法的框架,模板方法不只能夠調用在抽象類中實現的基本方法,也能夠調用在抽象類的子類中實現的基本方法,還能夠調用其餘對象中的方法。
(2) ConcreteClass(具體子類):它是抽象類的子類,用於實如今父類中聲明的抽象基本操做以完成子類特定算法的步驟,也能夠覆蓋在父類中已經實現的具體基本操做。
---------------------
AbstractClass的方法 分爲
1 模板方法( 子類是不能動的!),
一個模板方法是定義在抽象類中的、把基本操做方法組合在一塊兒造成一個總算法或一個總行爲的方法。這個模板方法定義在抽象類中,並由子類不加以修改地徹底繼承下來。模板方法是一個具體方法,它給出了一個頂層邏輯框架,而邏輯的組成步驟在抽象類中能夠是具體方法,也能夠是抽象方法。因爲模板方法是具體方法,所以模板方法模式中的抽象層只能是抽象類,而不是接口。
---------------------
2 基本方法。基本方法是實現算法各個步驟的方法,是模板方法的組成部分。基本方法又能夠分爲三種:抽象方法(Abstract Method)、具體方法(Concrete Method)和鉤子方法(Hook Method)。
模板方法模式效果與適用場景
模板方法模式是基於繼承的代碼複用技術,它體現了面向對象的諸多重要思想,是一種使用較爲頻繁的模式。模板方法模式普遍應用於框架設計中,以確保經過父類來控制處理流程的邏輯順序(如框架的初始化,測試流程的設置等)。
5.1 模式優勢
模板方法模式的主要優勢以下:
(1) 在父類中形式化地定義一個算法,而由它的子類來實現細節的處理,在子類實現詳細的處理算法時並不會改變算法中步驟的執行次序。
(2) 模板方法模式是一種代碼複用技術,它在類庫設計中尤其重要,它提取了類庫中的公共行爲,將公共行爲放在父類中,而經過其子類來實現不一樣的行爲,它鼓勵咱們恰當使用繼承來實現代碼複用。
(3) 可實現一種反向控制結構,經過子類覆蓋父類的鉤子方法來決定某一特定步驟是否須要執行。
(4) 在模板方法模式中能夠經過子類來覆蓋父類的基本方法,不一樣的子類能夠提供基本方法的不一樣實現,更換和增長新的子類很方便,符合單一職責原則和開閉原則。
5.2 模式缺點
模板方法模式的主要缺點以下:
須要爲每個基本方法的不一樣實現提供一個子類,若是父類中可變的基本方法太多,將會致使類的個數增長,系統更加龐大,設計也更加抽象,此時,可結合橋接模式來進行設計。
5.3 模式適用場景
在如下狀況下能夠考慮使用模板方法模式:
(1) 對一些複雜的算法進行分割,將其算法中固定不變的部分設計爲模板方法和父類具體方法,而一些能夠改變的細節由其子類來實現。即:一次性實現一個算法的不變部分,並將可變的行爲留給子類來實現。
(2) 各子類中公共的行爲應被提取出來並集中到一個公共父類中以免代碼重複。
(3) 須要經過子類來決定父類算法中某個步驟是否執行,實現子類對父類的反向控制。
---------------------
訪問者模式是一種較爲複雜的行爲型設計模式,它包含訪問者和被訪問元素兩個主要組成部分,這些被訪問的元素一般具備不一樣的類型,且不一樣的訪問者能夠對它們進行不一樣的訪問操做。例如處方單中的各類藥品信息就是被訪問的元素,而劃價人員和藥房工做人員就是訪問者。訪問者模式使得用戶能夠在不修改現有系統的狀況下擴展系統的功能,爲這些不一樣類型的元素增長新的操做。
在使用訪問者模式時,被訪問元素一般不是單獨存在的,它們存儲在一個集合中,這個集合被稱爲「對象結構」,訪問者經過遍歷對象結構實現對其中存儲的元素的逐個操做。
訪問者模式定義以下:
訪問者模式(Visitor Pattern):提供一個做用於某對象結構中的各元素的操做表示,它使咱們能夠在不改變各元素的類的前提下定義做用於這些元素的新操做。訪問者模式是一種對象行爲型模式。
訪問者模式的結構較爲複雜
---------------------
●Vistor(抽象訪問者):抽象訪問者爲對象結構中每個具體元素類ConcreteElement聲明一個訪問操做,從這個操做的名稱或參數類型能夠清楚知道須要訪問的具體元素的類型,具體訪問者須要實現這些操做方法,定義對這些元素的訪問操做。
●ConcreteVisitor(具體訪問者):具體訪問者實現了每一個由抽象訪問者聲明的操做,每個操做用於訪問對象結構中一種類型的元素。
●Element(抽象元素):抽象元素通常是抽象類或者接口,它定義一個accept()方法,該方法一般以一個抽象訪問者做爲參數。【稍後將介紹爲何要這樣設計。】
●ConcreteElement(具體元素):具體元素實現了accept()方法,在accept()方法中調用訪問者的訪問方法以便完成對一個元素的操做。
● ObjectStructure(對象結構):對象結構是一個元素的集合,它用於存放元素對象,而且提供了遍歷其內部元素的方法。它能夠結合組合模式來實現,也能夠是一個簡單的集合對象,如一個List對象或一個Set對象。
訪問者模式中對象結構存儲了不一樣類型的元素對象,以供不一樣訪問者訪問。訪問者模式包括兩個層次結構,一個是訪問者層次結構,提供了抽象訪問者和具體訪問者,一個是元素層次結構,提供了抽象元素和具體元素。相同的訪問者能夠以不一樣的方式訪問不一樣的元素,相同的元素能夠接受不一樣訪問者以不一樣訪問方式訪問。在訪問者模式中,增長新的訪問者無須修改原有系統,系統具備較好的可擴展性。
在訪問者模式中,抽象訪問者定義了訪問元素對象的方法,一般爲每一種類型的元素對象都提供一個訪問方法,而具體訪問者能夠實現這些訪問方法。這些訪問方法的命名通常有兩種方式:一種是直接在方法名中標明待訪問元素對象的具體類型,如visitElementA(ElementA elementA),還有一種是統一取名爲visit(),經過參數類型的不一樣來定義一系列重載的visit()方法。固然,若是全部的訪問者對某一類型的元素的訪問操做都相同,則能夠將操做代碼移到抽象訪問者類中
---------------------
在具體元素類ConcreteElementA的accept()方法中,經過調用Visitor類的visit()方法實現對元素的訪問,並以當前對象做爲visit()方法的參數。其具體執行過程以下:
(1) 調用具體元素類的accept(Visitor visitor)方法,並將Visitor子類對象做爲其參數;
(2) 在具體元素類accept(Visitor visitor)方法內部調用傳入的Visitor對象的visit()方法,如visit(ConcreteElementA elementA),將當前具體元素類對象(this)做爲參數,如visitor.visit(this);
(3) 執行Visitor對象的visit()方法,在其中還能夠調用具體元素對象的業務方法。
這種調用機制也稱爲「雙重分派」,正由於使用了雙重分派機制,使得增長新的訪問者無須修改現有類庫代碼,只需將新的訪問者對象做爲參數傳入具體元素對象的accept()方法,程序運行時將回調在新增Visitor類中定義的visit()方法,從而增長新的元素訪問方式
---------------------
訪問者模式是否符合「開閉原則」?【從增長新的訪問者和增長新的元素兩方面考慮。】 新的訪問者, 是的; 新的元素, 不是的!
在訪問者模式中,包含一個用於存儲元素對象集合的對象結構,咱們一般可使用迭代器來遍歷對象結構,同時具體元素之間能夠存在總體與部分關係,有些元素做爲容器對象,有些元素做爲成員對象,可使用組合模式來組織元素。引入組合模式後的訪問者模式結構圖如圖
訪問者模式總結
因爲訪問者模式的使用條件較爲苛刻,自己結構也較爲複雜,所以在實際應用中使用頻率不是特別高。當系統中存在一個較爲複雜的對象結構,且不一樣訪問者對其所採起的操做也不相同時,能夠考慮使用訪問者模式進行設計。在XML文檔解析、編譯器的設計、複雜集合對象的處理等領域訪問者模式獲得了必定的應用。
1.主要優勢
訪問者模式的主要優勢以下:
(1) 增長新的訪問操做很方便。使用訪問者模式,增長新的訪問操做就意味着增長一個新的具體訪問者類,實現簡單,無須修改源代碼,符合「開閉原則」。
(2) 將有關元素對象的訪問行爲集中到一個訪問者對象中,而不是分散在一個個的元素類中。類的職責更加清晰,有利於對象結構中元素對象的複用,相同的對象結構能夠供多個不一樣的訪問者訪問。
(3) 讓用戶可以在不修改現有元素類層次結構的狀況下,定義做用於該層次結構的操做。
2.主要缺點
訪問者模式的主要缺點以下:
(1) 增長新的元素類很困難。在訪問者模式中,每增長一個新的元素類都意味着要在抽象訪問者角色中增長一個新的抽象操做,並在每個具體訪問者類中增長相應的具體操做,這違背了「開閉原則」的要求。
(2) 破壞封裝。訪問者模式要求訪問者對象訪問並調用每個元素對象的操做,這意味着元素對象有時候必須暴露一些本身的內部操做和內部狀態,不然沒法供訪問者訪問。
3.適用場景
在如下狀況下能夠考慮使用訪問者模式:
(1) 一個對象結構包含多個類型的對象,但願對這些對象實施一些依賴其具體類型的操做。在訪問者中針對每一種具體的類型都提供了一個訪問操做,不一樣類型的對象能夠有不一樣的訪問操做。
(2) 須要對一個對象結構中的對象進行不少不一樣的而且不相關的操做,而須要避免讓這些操做「污染」這些對象的類,也不但願在增長新操做時修改這些類。訪問者模式使得咱們能夠將相關的訪問操做集中起來定義在訪問者類中,對象結構能夠被多個不一樣的訪問者類所使用,將對象自己與對象的訪問操做分離。
(3) 對象結構中對象對應的類不多改變,但常常須要在此對象結構上定義新的操做。
---------------------
不得不說的是 ObjectStructure, 這個有點奇怪,對象結構? 咋理解?咱們看到ObjectStructure依賴了Element,Client是直接和ObjectStructure 交互的, 也就是Client經過ObjectStructure 選擇了 具體的Element。
========================================== THE END ==============================================
本文大部分摘抄於:https://blog.csdn.net/lovelion