設計模式之行為型

設計模式概述

基于設計原則苹支,GoF(設計模式總結(jié)4人組)總結(jié)了軟件開發(fā)領域的23個經(jīng)典設計模式。雖然GoF設計模式只有23個误阻,但是它們各具特色债蜜,每個模式都為某一個可重復的設計問題提供了一套解決方案。根據(jù)它們的用途究反,設計模式可分為創(chuàng)建型(Creational)寻定,結(jié)構型(Structural)和行為型(Behavioral)三種,其中創(chuàng)建型模式主要用于描述如何創(chuàng)建對象精耐,結(jié)構型模式主要用于描述如何實現(xiàn)類或?qū)ο蟮慕M合狼速,行為型模式主要用于描述類或?qū)ο笤鯓咏换ヒ约霸鯓臃峙渎氊煟贕oF 23種設計模式中包含5種創(chuàng)建型設計模式卦停、7種結(jié)構型設計模式和11種行為型設計模式向胡。此外,根據(jù)某個模式主要是用于處理類之間的關系還是對象之間的關系惊完,設計模式還可以分為類模式和對象模式羊壹。我們經(jīng)常將兩種分類方式結(jié)合使用棺弊,如單例模式是對象創(chuàng)建型模式,模板方法模式是類行為型模式厢漩。
值得一提的是电爹,有一個設計模式雖然不屬于GoF 23種設計模式伟阔,但一般在介紹設計模式時都會對它進行說明良拼,它就是簡單工廠模式僵驰,也許是太“簡單”了,GoF并沒有把它寫到那本經(jīng)典著作中拄显,不過現(xiàn)在大部分的設計模式書籍都會對它進行專門的介紹苟径。

                                 常用設計模式一覽表
類型 模式名稱 學習難度 使用頻率
創(chuàng)建型模式(Creational Pattern) 單例模式(Singleton Pattern) ★☆☆☆☆ ★★★★☆
創(chuàng)建型模式(Creational Pattern) 簡單工廠模式(Simple Factory Pattern) ★★☆☆☆ ★★★☆☆
創(chuàng)建型模式(Creational Pattern) 工廠方法模式(Factory Method Pattern) ★★☆☆☆ ★★★★★
創(chuàng)建型模式(Creational Pattern) 抽象工廠模式(Abstract Factory Pattern) ★★★★☆ ★★★★★
創(chuàng)建型模式(Creational Pattern) 原型模式(Prototype Pattern) ★★★☆☆ ★★★☆☆
創(chuàng)建型模式(Creational Pattern) 建造者模式(Builder Pattern) ★★★★☆ ★★☆☆☆
結(jié)構型模式(Structural Pattern) 適配器模式(Adapter Pattern) ★★☆☆☆ ★★★★☆
結(jié)構型模式(Structural Pattern) 橋接模式(Bridge Pattern) ★★★☆☆ ★★★☆☆
結(jié)構型模式(Structural Pattern) 組合模式(Composite Pattern) ★★★☆☆ ★★★★☆
結(jié)構型模式(Structural Pattern) 裝飾模式(Decorator Pattern) ★★★☆☆ ★★★☆☆
結(jié)構型模式(Structural Pattern) 外觀模式(Fa?ade Pattern) ★☆☆☆☆ ★★★★★
結(jié)構型模式(Structural Pattern) 享元模式(Flyweight Pattern) ★★★★☆ ★☆☆☆☆
結(jié)構型模式(Structural Pattern) 代理模式(Proxy Pattern) ★★★☆☆ ★★★★☆
行為型模式(Behavioral Pattern) 職責鏈模式(Chain of Responsibility Pattern) ★★★☆☆ ★★☆☆☆
行為型模式(Behavioral Pattern) 命令模式(Command Pattern) ★★★☆☆ ★★★★☆
行為型模式(Behavioral Pattern) 解釋器模式(Interpreter Pattern) ★★★★★ ★☆☆☆☆
行為型模式(Behavioral Pattern) 迭代器模式(Iterator Pattern) ★★★☆☆ ★★★★★
行為型模式(Behavioral Pattern) 中介者模式(Mediator Pattern) ★★★☆☆ ★★☆☆☆
行為型模式(Behavioral Pattern) 備忘錄模式(Memento Pattern) ★★☆☆☆ ★★☆☆☆
行為型模式(Behavioral Pattern) 觀察者模式(Observer Pattern) ★★★☆☆ ★★★★★
行為型模式(Behavioral Pattern) 狀態(tài)模式(State Pattern) ★★★☆☆ ★★★☆☆
行為型模式(Behavioral Pattern) 策略模式(Strategy Pattern) ★☆☆☆☆ ★★★★☆
行為型模式(Behavioral Pattern) 模板方法模式(Template Method Pattern) ★★☆☆☆ ★★★☆☆
行為型模式(Behavioral Pattern) 訪問者模式(Visitor Pattern) ★★★★☆ ★☆☆☆☆

設計模式詳解

職責鏈模式-Chain of Responsibility Pattern

職責鏈模式(Chain of Responsibility Pattern):避免請求發(fā)送者與接收者耦合在一起,讓多個對象都有可能接收請求躬审,將這些對象連接成一條鏈,并且沿著這條鏈傳遞請求蟆盐,直到有對象處理它為止承边。職責鏈模式是一種對象行為型模式。
職責鏈模式結(jié)構的核心在于引入了一個抽象處理者石挂。職責鏈模式結(jié)構如下圖所示:


職責鏈模式結(jié)構圖

在職責鏈模式結(jié)構圖中包含如下幾個角色:
● Handler(抽象處理者):它定義了一個處理請求的接口博助,一般設計為抽象類,由于不同的具體處理者處理請求的方式不同痹愚,因此在其中定義了抽象請求處理方法富岳。因為每一個處理者的下家還是一個處理者蛔糯,因此在抽象處理者中定義了一個抽象處理者類型的對象(如結(jié)構圖中的successor),作為其對下家的引用窖式。通過該引用蚁飒,處理者可以連成一條鏈。
● ConcreteHandler(具體處理者):它是抽象處理者的子類萝喘,可以處理用戶請求淮逻,在具體處理者類中實現(xiàn)了抽象處理者中定義的抽象請求處理方法,在處理請求之前需要進行判斷阁簸,看是否有相應的處理權限爬早,如果可以處理請求就處理它,否則將請求轉(zhuǎn)發(fā)給后繼者启妹;在具體處理者中可以訪問鏈中下一個對象筛严,以便請求的轉(zhuǎn)發(fā)。
在職責鏈模式里饶米,很多對象由每一個對象對其下家的引用而連接起來形成一條鏈桨啃。請求在這個鏈上傳遞,直到鏈上的某一個對象決定處理此請求咙崎。發(fā)出這個請求的客戶端并不知道鏈上的哪一個對象最終處理這個請求优幸,這使得系統(tǒng)可以在不影響客戶端的情況下動態(tài)地重新組織鏈和分配責任。
需要注意的是褪猛,職責鏈模式并不創(chuàng)建職責鏈网杆,職責鏈的創(chuàng)建工作必須由系統(tǒng)的其他部分來完成,一般是在使用該職責鏈的客戶端中創(chuàng)建職責鏈伊滋。職責鏈模式降低了請求的發(fā)送端和接收端之間的耦合碳却,使多個對象都有機會處理這個請求。

職責鏈模式總結(jié)
職責鏈模式通過建立一條鏈來組織請求的處理者笑旺,請求將沿著鏈進行傳遞昼浦,請求發(fā)送者無須知道請求在何時、何處以及如何被處理筒主,實現(xiàn)了請求發(fā)送者與處理者的解耦关噪。在軟件開發(fā)中,如果遇到有多個對象可以處理同一請求時可以應用職責鏈模式乌妙,例如在Web應用開發(fā)中創(chuàng)建一個過濾器(Filter)鏈來對請求數(shù)據(jù)進行過濾使兔,在工作流系統(tǒng)中實現(xiàn)公文的分級審批等等,使用職責鏈模式可以較好地解決此類問題藤韵。
1.主要優(yōu)點
職責鏈模式的主要優(yōu)點如下:
(1) 職責鏈模式使得一個對象無須知道是其他哪一個對象處理其請求虐沥,對象僅需知道該請求會被處理即可,接收者和發(fā)送者都沒有對方的明確信息,且鏈中的對象不需要知道鏈的結(jié)構欲险,由客戶端負責鏈的創(chuàng)建镐依,降低了系統(tǒng)的耦合度。
(2) 請求處理對象僅需維持一個指向其后繼者的引用天试,而不需要維持它對所有的候選處理者的引用槐壳,可簡化對象的相互連接。
(3) 在給對象分派職責時秋秤,職責鏈可以給我們更多的靈活性宏粤,可以通過在運行時對該鏈進行動態(tài)的增加或修改來增加或改變處理一個請求的職責。
(4) 在系統(tǒng)中增加一個新的具體請求處理者時無須修改原有系統(tǒng)的代碼灼卢,只需要在客戶端重新建鏈即可绍哎,從這一點來看是符合“開閉原則”的。
2.主要缺點
職責鏈模式的主要缺點如下:
(1) 由于一個請求沒有明確的接收者鞋真,那么就不能保證它一定會被處理崇堰,該請求可能一直到鏈的末端都得不到處理;一個請求也可能因職責鏈沒有被正確配置而得不到處理涩咖。
(2) 對于比較長的職責鏈海诲,請求的處理可能涉及到多個處理對象,系統(tǒng)性能將受到一定影響檩互,而且在進行代碼調(diào)試時不太方便特幔。
(3) 如果建鏈不當,可能會造成循環(huán)調(diào)用闸昨,將導致系統(tǒng)陷入死循環(huán)蚯斯。
3.適用場景
在以下情況下可以考慮使用職責鏈模式:
(1) 有多個對象可以處理同一個請求,具體哪個對象處理該請求待運行時刻再確定饵较,客戶端只需將請求提交到鏈上拍嵌,而無須關心請求的處理對象是誰以及它是如何處理的。
(2) 在不明確指定接收者的情況下循诉,向多個對象中的一個提交一個請求横辆。
(3) 可動態(tài)指定一組對象處理請求,客戶端可以動態(tài)創(chuàng)建職責鏈來處理請求茄猫,還可以改變鏈中處理者之間的先后次序狈蚤。

命令模式-Command Pattern

在軟件開發(fā)中,我們經(jīng)常需要向某些對象發(fā)送請求(調(diào)用其中的某個或某些方法)划纽,但是并不知道請求的接收者是誰炫惩,也不知道被請求的操作是哪個,此時阿浓,我們特別希望能夠以一種松耦合的方式來設計軟件,使得請求發(fā)送者與請求接收者能夠消除彼此之間的耦合蹋绽,讓對象之間的調(diào)用關系更加靈活芭毙,可以靈活地指定請求接收者以及被請求的操作筋蓖。命令模式為此類問題提供了一個較為完美的解決方案。
命令模式可以將請求發(fā)送者和接收者完全解耦退敦,發(fā)送者與接收者之間沒有直接引用關系粘咖,發(fā)送請求的對象只需要知道如何發(fā)送請求,而不必知道如何完成請求侈百。
命令模式(Command Pattern):將一個請求封裝為一個對象瓮下,從而讓我們可用不同的請求對客戶進行參數(shù)化;對請求排隊或者記錄請求日志钝域,以及支持可撤銷的操作讽坏。命令模式是一種對象行為型模式,其別名為動作(Action)模式或事務(Transaction)模式例证。
命令模式的定義比較復雜路呜,提到了很多術語,例如“用不同的請求對客戶進行參數(shù)化”织咧、“對請求排隊”胀葱,“記錄請求日志”、“支持可撤銷操作”等笙蒙,在后面我們將對這些術語進行一一講解抵屿。
命令模式的核心在于引入了命令類,通過命令類來降低發(fā)送者和接收者的耦合度捅位,請求發(fā)送者只需指定一個命令對象轧葛,再通過命令對象來調(diào)用請求接收者的處理方法,其結(jié)構如下圖所示:


命令模式結(jié)構圖

在命令模式結(jié)構圖中包含如下幾個角色:
● Command(抽象命令類):抽象命令類一般是一個抽象類或接口绿渣,在其中聲明了用于執(zhí)行請求的execute()等方法朝群,通過這些方法可以調(diào)用請求接收者的相關操作。
● ConcreteCommand(具體命令類):具體命令類是抽象命令類的子類中符,實現(xiàn)了在抽象命令類中聲明的方法姜胖,它對應具體的接收者對象,將接收者對象的動作綁定其中淀散。在實現(xiàn)execute()方法時右莱,將調(diào)用接收者對象的相關操作(Action)。
● Invoker(調(diào)用者):調(diào)用者即請求發(fā)送者档插,它通過命令對象來執(zhí)行請求慢蜓。一個調(diào)用者并不需要在設計時確定其接收者,因此它只與抽象命令類之間存在關聯(lián)關系郭膛。在程序運行時可以將一個具體命令對象注入其中晨抡,再調(diào)用具體命令對象的execute()方法,從而實現(xiàn)間接調(diào)用請求接收者的相關操作。
● Receiver(接收者):接收者執(zhí)行與請求相關的操作耘柱,它具體實現(xiàn)對請求的業(yè)務處理如捅。
命令模式的本質(zhì)是對請求進行封裝,一個請求對應于一個命令调煎,將發(fā)出命令的責任和執(zhí)行命令的責任分割開镜遣。每一個命令都是一個操作:請求的一方發(fā)出請求要求執(zhí)行一個操作;接收的一方收到請求士袄,并執(zhí)行相應的操作悲关。命令模式允許請求的一方和接收的一方獨立開來,使得請求的一方不必知道接收請求的一方的接口娄柳,更不必知道請求如何被接收寓辱、操作是否被執(zhí)行、何時被執(zhí)行西土,以及是怎么被執(zhí)行的讶舰。
命令模式的關鍵在于引入了抽象命令類,請求發(fā)送者針對抽象命令類編程需了,只有實現(xiàn)了抽象命令類的具體命令才與請求接收者相關聯(lián)跳昼。在最簡單的抽象命令類中只包含了一個抽象的execute()方法,每個具體命令類將一個Receiver類型的對象作為一個實例變量進行存儲肋乍,從而具體指定一個請求的接收者鹅颊,不同的具體命令類提供了execute()方法的不同實現(xiàn),并調(diào)用不同接收者的請求處理方法墓造。

宏命令
宏命令(Macro Command)又稱為組合命令堪伍,它是組合模式和命令模式聯(lián)用的產(chǎn)物。宏命令是一個具體命令類觅闽,它擁有一個集合屬性帝雇,在該集合中包含了對其他命令對象的引用。通常宏命令不直接與請求接收者交互蛉拙,而是通過它的成員來調(diào)用接收者的方法尸闸。當調(diào)用宏命令的execute()方法時,將遞歸調(diào)用它所包含的每個成員命令的execute()方法孕锄,一個宏命令的成員可以是簡單命令吮廉,還可以繼續(xù)是宏命令。執(zhí)行一個宏命令將觸發(fā)多個具體命令的執(zhí)行畸肆,從而實現(xiàn)對命令的批處理宦芦,其結(jié)構如下圖所示:

宏命令結(jié)構圖

命令模式總結(jié)
命令模式是一種使用頻率非常高的設計模式,它可以將請求發(fā)送者與接收者解耦轴脐,請求發(fā)送者通過命令對象來間接引用請求接收者调卑,使得系統(tǒng)具有更好的靈活性和可擴展性抡砂。在基于GUI的軟件開發(fā),無論是在電腦桌面應用還是在移動應用中令野,命令模式都得到了廣泛的應用舀患。

  1. 主要優(yōu)點
    命令模式的主要優(yōu)點如下:
    (1) 降低系統(tǒng)的耦合度。由于請求者與接收者之間不存在直接引用气破,因此請求者與接收者之間實現(xiàn)完全解耦,相同的請求者可以對應不同的接收者餐抢,同樣现使,相同的接收者也可以供不同的請求者使用,兩者之間具有良好的獨立性旷痕。
    (2) 新的命令可以很容易地加入到系統(tǒng)中碳锈。由于增加新的具體命令類不會影響到其他類,因此增加新的具體命令類很容易欺抗,無須修改原有系統(tǒng)源代碼售碳,甚至客戶類代碼,滿足“開閉原則”的要求绞呈。
    (3) 可以比較容易地設計一個命令隊列或宏命令(組合命令)贸人。
    (4) 為請求的撤銷(Undo)和恢復(Redo)操作提供了一種設計和實現(xiàn)方案。
  2. 主要缺點
    命令模式的主要缺點如下:
    使用命令模式可能會導致某些系統(tǒng)有過多的具體命令類佃声。因為針對每一個對請求接收者的調(diào)用操作都需要設計一個具體命令類艺智,因此在某些系統(tǒng)中可能需要提供大量的具體命令類,這將影響命令模式的使用圾亏。
  3. 適用場景
    在以下情況下可以考慮使用命令模式:
    (1) 系統(tǒng)需要將請求調(diào)用者和請求接收者解耦十拣,使得調(diào)用者和接收者不直接交互。請求調(diào)用者無須知道接收者的存在志鹃,也無須知道接收者是誰夭问,接收者也無須關心何時被調(diào)用。
    (2) 系統(tǒng)需要在不同的時間指定請求曹铃、將請求排隊和執(zhí)行請求缰趋。一個命令對象和請求的初始調(diào)用者可以有不同的生命期,換言之铛只,最初的請求發(fā)出者可能已經(jīng)不在了埠胖,而命令對象本身仍然是活動的,可以通過該命令對象去調(diào)用請求接收者淳玩,而無須關心請求調(diào)用者的存在性直撤,可以通過請求日志文件等機制來具體實現(xiàn)。
    (3) 系統(tǒng)需要支持命令的撤銷(Undo)操作和恢復(Redo)操作蜕着。
    (4) 系統(tǒng)需要將一組操作組合在一起形成宏命令谋竖。

解釋器模式-Interpreter Pattern

文法規(guī)則和抽象語法樹
解釋器模式描述了如何為簡單的語言定義一個文法红柱,如何在該語言中表示一個句子,以及如何解釋這些句子蓖乘。在正式分析解釋器模式結(jié)構之前锤悄,我們先來學習如何表示一個語言的文法規(guī)則以及如何構造一棵抽象語法樹。例如“1 + 2 + 3 – 4 + 1”嘉抒,可以使用如下文法規(guī)則來定義:

expression ::= value | operation
operation ::= expression '+' expression | expression '-'  expression
value ::= an integer //一個整數(shù)值

該文法規(guī)則包含三條語句零聚,第一條表示表達式的組成方式,其中value和operation是后面兩個語言單位的定義些侍,每一條語句所定義的字符串如operation和value稱為語言構造成分或語言單位隶症,符號“::=”表示“定義為”的意思,其左邊的語言單位通過右邊來進行說明和定義岗宣,語言單位對應終結(jié)符表達式和非終結(jié)符表達式蚂会。如本規(guī)則中的operation是非終結(jié)符表達式,它的組成元素仍然可以是表達式耗式,可以進一步分解胁住,而value是終結(jié)符表達式,它的組成元素是最基本的語言單位刊咳,不能再進行分解彪见。
在文法規(guī)則定義中可以使用一些符號來表示不同的含義,如使用“|”表示或芦缰,使用“{”和“}”表示組合企巢,使用“*”表示出現(xiàn)0次或多次等,其中使用頻率最高的符號是表示“或”關系的“|”让蕾,如文法規(guī)則“boolValue ::= 0 | 1”表示終結(jié)符表達式boolValue的取值可以為0或者1浪规。
除了使用文法規(guī)則來定義一個語言,在解釋器模式中還可以通過一種稱之為抽象語法樹(Abstract Syntax Tree, AST)的圖形方式來直觀地表示語言的構成探孝,每一棵抽象語法樹對應一個語言實例笋婿,如加法/減法表達式語言中的語句“1+ 2 + 3 – 4 + 1”,可以通過如下圖所示抽象語法樹來表示:


抽象語法樹示意圖

在該抽象語法樹中顿颅,可以通過終結(jié)符表達式value和非終結(jié)符表達式operation組成復雜的語句缸濒,每個文法規(guī)則的語言實例都可以表示為一個抽象語法樹,即每一條具體的語句都可以用類似上圖所示的抽象語法樹來表示粱腻,在圖中終結(jié)符表達式類的實例作為樹的葉子節(jié)點庇配,而非終結(jié)符表達式類的實例作為非葉子節(jié)點,它們可以將終結(jié)符表達式類的實例以及包含終結(jié)符和非終結(jié)符實例的子表達式作為其子節(jié)點绍些。抽象語法樹描述了如何構成一個復雜的句子捞慌,通過對抽象語法樹的分析,可以識別出語言中的終結(jié)符類和非終結(jié)符類柬批。

解釋器模式
解釋器模式是一種使用頻率相對較低但學習難度較大的設計模式啸澡,它用于描述如何使用面向?qū)ο笳Z言構成一個簡單的語言解釋器袖订。在某些情況下,為了更好地描述某一些特定類型的問題嗅虏,我們可以創(chuàng)建一種新的語言洛姑,這種語言擁有自己的表達式和結(jié)構茬暇,即文法規(guī)則揩瞪,這些問題的實例將對應為該語言中的句子。此時犀农,可以使用解釋器模式來設計這種新的語言冰更。對解釋器模式的學習能夠加深我們對面向?qū)ο笏枷氲睦斫獠玻⑶艺莆站幊陶Z言中文法規(guī)則的解釋過程。
解釋器模式(Interpreter Pattern):定義一個語言的文法蜀细,并且建立一個解釋器來解釋該語言中的句子,這里的“語言”是指使用規(guī)定格式和語法的代碼戈盈。解釋器模式是一種類行為型模式奠衔。
由于表達式可分為終結(jié)符表達式和非終結(jié)符表達式,因此解釋器模式的結(jié)構與組合模式的結(jié)構有些類似塘娶,但在解釋器模式中包含更多的組成元素归斤,它的結(jié)構如下圖所示:

解釋器模式結(jié)構圖

在解釋器模式結(jié)構圖中包含如下幾個角色:
● AbstractExpression(抽象表達式):在抽象表達式中聲明了抽象的解釋操作,它是所有終結(jié)符表達式和非終結(jié)符表達式的公共父類刁岸。
● TerminalExpression(終結(jié)符表達式):終結(jié)符表達式是抽象表達式的子類脏里,它實現(xiàn)了與文法中的終結(jié)符相關聯(lián)的解釋操作,在句子中的每一個終結(jié)符都是該類的一個實例虹曙。通常在一個解釋器模式中只有少數(shù)幾個終結(jié)符表達式類迫横,它們的實例可以通過非終結(jié)符表達式組成較為復雜的句子。
● NonterminalExpression(非終結(jié)符表達式):非終結(jié)符表達式也是抽象表達式的子類酝碳,它實現(xiàn)了文法中非終結(jié)符的解釋操作矾踱,由于在非終結(jié)符表達式中可以包含終結(jié)符表達式,也可以繼續(xù)包含非終結(jié)符表達式疏哗,因此其解釋操作一般通過遞歸的方式來完成呛讲。
● Context(環(huán)境類):環(huán)境類又稱為上下文類,它用于存儲解釋器之外的一些全局信息返奉,通常它臨時存儲了需要解釋的語句贝搁。
最后還需要一個工具類用于對輸入指令進行處理,將輸入指令按照優(yōu)先級語法解析成符號表達式存入棧中芽偏,再從棧中取出按約定語法構成的能夠解析的符號表達式雷逆,逐個解析。

解釋器模式總結(jié)
解釋器模式為自定義語言的設計和實現(xiàn)提供了一種解決方案哮针,它用于定義一組文法規(guī)則并通過這組文法規(guī)則來解釋語言中的句子关面。雖然解釋器模式的使用頻率不是特別高坦袍,但是它在正則表達式、XML文檔解釋等領域還是得到了廣泛使用等太。與解釋器模式類似捂齐,目前還誕生了很多基于抽象語法樹的源代碼處理工具,例如Mysql的語法解析工具druid等缩抡,它可以用于表示Java語言的語法結(jié)構奠宜,用戶可以通過擴展其功能,創(chuàng)建自己的文法規(guī)則瞻想。

  1. 主要優(yōu)點
    解釋器模式的主要優(yōu)點如下:
    (1) 易于改變和擴展文法压真。由于在解釋器模式中使用類來表示語言的文法規(guī)則,因此可以通過繼承等機制來改變或擴展文法蘑险。
    (2) 每一條文法規(guī)則都可以表示為一個類滴肿,因此可以方便地實現(xiàn)一個簡單的語言。
    (3) 實現(xiàn)文法較為容易佃迄。在抽象語法樹中每一個表達式節(jié)點類的實現(xiàn)方式都是相似的泼差,這些類的代碼編寫都不會特別復雜,還可以通過一些工具自動生成節(jié)點類代碼呵俏。
    (4) 增加新的解釋表達式較為方便堆缘。如果用戶需要增加新的解釋表達式只需要對應增加一個新的終結(jié)符表達式或非終結(jié)符表達式類,原有表達式類代碼無須修改普碎,符合“開閉原則”吼肥。
  2. 主要缺點
    解釋器模式的主要缺點如下:
    (1) 對于復雜文法難以維護。在解釋器模式中麻车,每一條規(guī)則至少需要定義一個類缀皱,因此如果一個語言包含太多文法規(guī)則,類的個數(shù)將會急劇增加绪氛,導致系統(tǒng)難以管理和維護唆鸡,此時可以考慮使用語法分析程序等方式來取代解釋器模式。
    (2) 執(zhí)行效率較低枣察。由于在解釋器模式中使用了大量的循環(huán)和遞歸調(diào)用争占,因此在解釋較為復雜的句子時其速度很慢,而且代碼的調(diào)試過程也比較麻煩序目。
  3. 適用場景
    在以下情況下可以考慮使用解釋器模式:
    (1) 可以將一個需要解釋執(zhí)行的語言中的句子表示為一個抽象語法樹臂痕。
    (2) 一些重復出現(xiàn)的問題可以用一種簡單的語言來進行表達。
    (3) 一個語言的文法較為簡單猿涨。
    (4) 執(zhí)行效率不是關鍵問題握童。【注:高效的解釋器通常不是通過直接解釋抽象語法樹來實現(xiàn)的叛赚,而是需要將它們轉(zhuǎn)換成其他形式澡绩,使用解釋器模式的執(zhí)行效率并不高稽揭。】

迭代器模式-Iterator Pattern

在軟件開發(fā)中肥卡,我們經(jīng)常需要使用聚合對象來存儲一系列數(shù)據(jù)溪掀。聚合對象擁有兩個職責:一是存儲數(shù)據(jù);二是遍歷數(shù)據(jù)步鉴。從依賴性來看揪胃,前者是聚合對象的基本職責;而后者既是可變化的氛琢,又是可分離的喊递。因此,可以將遍歷數(shù)據(jù)的行為從聚合對象中分離出來阳似,封裝在一個被稱之為“迭代器”的對象中骚勘,由迭代器來提供遍歷聚合對象內(nèi)部數(shù)據(jù)的行為,這將簡化聚合對象的設計撮奏,更符合“單一職責原則”的要求调鲸。
迭代器模式(Iterator Pattern):提供一種方法來訪問聚合對象,而不用暴露這個對象的內(nèi)部表示挽荡,其別名為游標(Cursor)。迭代器模式是一種對象行為型模式即供。
在迭代器模式結(jié)構中包含聚合和迭代器兩個層次結(jié)構定拟,考慮到系統(tǒng)的靈活性和可擴展性,在迭代器模式中應用了工廠方法模式逗嫡,其模式結(jié)構如下圖所示:

迭代器模式結(jié)構圖

在迭代器模式結(jié)構圖中包含如下幾個角色:
● Iterator(抽象迭代器):它定義了訪問和遍歷元素的接口青自,聲明了用于遍歷數(shù)據(jù)元素的方法,例如:用于獲取第一個元素的first()方法驱证,用于訪問下一個元素的next()方法延窜,用于判斷是否還有下一個元素的hasNext()方法,用于獲取當前元素的currentItem()方法等抹锄,在具體迭代器中將實現(xiàn)這些方法逆瑞。
● ConcreteIterator(具體迭代器):它實現(xiàn)了抽象迭代器接口,完成對聚合對象的遍歷伙单,同時在具體迭代器中通過游標來記錄在聚合對象中所處的當前位置获高,在具體實現(xiàn)時,游標通常是一個表示位置的非負整數(shù)吻育。
● Aggregate(抽象聚合類):它用于存儲和管理元素對象念秧,聲明一個createIterator()方法用于創(chuàng)建一個迭代器對象,充當抽象迭代器工廠角色布疼。
● ConcreteAggregate(具體聚合類):它實現(xiàn)了在抽象聚合類中聲明的createIterator()方法摊趾,該方法返回一個與該具體聚合類對應的具體迭代器ConcreteIterator實例币狠。
在迭代器模式中,提供了一個外部的迭代器來對聚合對象進行訪問和遍歷砾层,迭代器定義了一個訪問該聚合元素的接口漩绵,并且可以跟蹤當前遍歷的元素,了解哪些元素已經(jīng)遍歷過而哪些沒有梢为。迭代器的引入渐行,將使得對一個復雜聚合對象的操作變得簡單。
需要注意的是抽象迭代器接口的設計非常重要铸董,一方面需要充分滿足各種遍歷操作的要求祟印,盡量為各種遍歷方法都提供聲明,另一方面又不能包含太多方法粟害,接口中方法太多將給子類的實現(xiàn)帶來麻煩蕴忆。因此,可以考慮使用抽象類來設計抽象迭代器悲幅,在抽象類中為每一個方法提供一個空的默認實現(xiàn)套鹅。如果需要在具體迭代器中為聚合對象增加全新的遍歷操作,則必須修改抽象迭代器和具體迭代器的源代碼汰具,這將違反“開閉原則”卓鹿,因此在設計時要考慮全面,避免之后修改接口留荔。
在迭代器模式結(jié)構圖中吟孙,我們可以看到具體迭代器類和具體聚合類之間存在雙重關系,其中一個關系為關聯(lián)關系聚蝶,在具體迭代器中需要維持一個對具體聚合對象的引用杰妓,該關聯(lián)關系的目的是訪問存儲在聚合對象中的數(shù)據(jù),以便迭代器能夠?qū)@些數(shù)據(jù)進行遍歷操作碘勉。
使用內(nèi)部類實現(xiàn)迭代器
除了使用關聯(lián)關系外巷挥,為了能夠讓迭代器可以訪問到聚合對象中的數(shù)據(jù),我們還可以將迭代器類設計為聚合類的內(nèi)部類验靡,JDK中的迭代器類就是通過這種方法來實現(xiàn)的倍宾,如下AbstractList類代碼片段所示:

package java.util;
……
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
    ......
    private class Itr implements Iterator<E> {
        int cursor = 0;
        ......
}
……
}

無論使用哪種實現(xiàn)機制,客戶端代碼都是一樣的晴叨,也就是說客戶端無須關心具體迭代器對象的創(chuàng)建細節(jié)凿宾,只需通過調(diào)用工廠方法createIterator()即可得到一個可用的迭代器對象,這也是使用工廠方法模式的好處兼蕊,通過工廠來封裝對象的創(chuàng)建過程初厚,簡化了客戶端的調(diào)用。
JDK內(nèi)置迭代器
為了讓開發(fā)人員能夠更加方便地操作聚合對象,在Java产禾、C#等編程語言中都提供了內(nèi)置迭代器排作。在Java集合框架中,常用的List和Set等聚合類都繼承(或?qū)崿F(xiàn))了java.util.Collection接口亚情,在Collection接口中聲明了如下方法(部分):

package java.util;

public interface Collection<E> extends Iterable<E> {
   ……
boolean add(Object c);
boolean addAll(Collection c);
boolean remove(Object o);
boolean removeAll(Collection c);
boolean remainAll(Collection c); 
Iterator iterator();
……
}

除了包含一些增加元素和刪除元素的方法外妄痪,還提供了一個iterator()方法,用于返回一個Iterator迭代器對象楞件,以便遍歷聚合中的元素衫生;具體的Java聚合類可以通過實現(xiàn)該iterator()方法返回一個具體的Iterator對象。
JDK中定義了抽象迭代器接口Iterator土浸,代碼如下所示:

package java.util;
public interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}

其中罪针,hasNext()用于判斷聚合對象中是否還存在下一個元素,為了不拋出異常黄伊,在每次調(diào)用next()之前需先調(diào)用hasNext()泪酱,如果有可供訪問的元素,則返回true还最;next()方法用于將游標移至下一個元素墓阀,通過它可以逐個訪問聚合中的元素,它返回游標所越過的那個元素的引用拓轻;remove()方法用于刪除上次調(diào)用next()時所返回的元素斯撮。
在JDK中,Collection接口和Iterator接口充當了迭代器模式的抽象層扶叉,分別對應于抽象聚合類和抽象迭代器吮成,而Collection接口的子類充當了具體聚合類,下面以List為例加以說明辜梳,圖6列出了JDK中部分與List有關的類及它們之間的關系:


Java集合框架中部分類結(jié)構圖

迭代器模式總結(jié)
迭代器模式是一種使用頻率非常高的設計模式,通過引入迭代器可以將數(shù)據(jù)的遍歷功能從聚合對象中分離出來泳叠,聚合對象只負責存儲數(shù)據(jù)作瞄,而遍歷數(shù)據(jù)由迭代器來完成。由于很多編程語言的類庫都已經(jīng)實現(xiàn)了迭代器模式危纫,因此在實際開發(fā)中宗挥,我們只需要直接使用Java、C#等語言已定義好的迭代器即可种蝶,迭代器已經(jīng)成為我們操作聚合對象的基本工具之一契耿。

  1. 主要優(yōu)點
    迭代器模式的主要優(yōu)點如下:
    (1) 它支持以不同的方式遍歷一個聚合對象,在同一個聚合對象上可以定義多種遍歷方式螃征。在迭代器模式中只需要用一個不同的迭代器來替換原有迭代器即可改變遍歷算法搪桂,我們也可以自己定義迭代器的子類以支持新的遍歷方式。
    (2) 迭代器簡化了聚合類。由于引入了迭代器踢械,在原有的聚合對象中不需要再自行提供數(shù)據(jù)遍歷等方法酗电,這樣可以簡化聚合類的設計。
    (3) 在迭代器模式中内列,由于引入了抽象層撵术,增加新的聚合類和迭代器類都很方便,無須修改原有代碼话瞧,滿足“開閉原則”的要求嫩与。
  2. 主要缺點
    迭代器模式的主要缺點如下:
    (1) 由于迭代器模式將存儲數(shù)據(jù)和遍歷數(shù)據(jù)的職責分離,增加新的聚合類需要對應增加新的迭代器類交排,類的個數(shù)成對增加划滋,這在一定程度上增加了系統(tǒng)的復雜性。
    (2) 抽象迭代器的設計難度較大个粱,需要充分考慮到系統(tǒng)將來的擴展古毛,例如JDK內(nèi)置迭代器Iterator就無法實現(xiàn)逆向遍歷,如果需要實現(xiàn)逆向遍歷都许,只能通過其子類ListIterator等來實現(xiàn)稻薇,而ListIterator迭代器無法用于操作Set類型的聚合對象。在自定義迭代器時胶征,創(chuàng)建一個考慮全面的抽象迭代器并不是件很容易的事情塞椎。
  3. 適用場景
    在以下情況下可以考慮使用迭代器模式:
    (1) 訪問一個聚合對象的內(nèi)容而無須暴露它的內(nèi)部表示。將聚合對象的訪問與內(nèi)部數(shù)據(jù)的存儲分離睛低,使得訪問聚合對象時無須了解其內(nèi)部實現(xiàn)細節(jié)案狠。
    (2) 需要為一個聚合對象提供多種遍歷方式。
    (3) 為遍歷不同的聚合結(jié)構提供一個統(tǒng)一的接口钱雷,在該接口的實現(xiàn)類中為不同的聚合結(jié)構提供不同的遍歷方式骂铁,而客戶端可以一致性地操作該接口。

中介者模式-Mediator Pattern

如果在一個系統(tǒng)中對象之間的聯(lián)系呈現(xiàn)為網(wǎng)狀結(jié)構罩抗。對象之間存在大量的多對多聯(lián)系拉庵,將導致系統(tǒng)非常復雜,這些對象既會影響別的對象套蒂,也會被別的對象所影響钞支,這些對象稱為同事對象,它們之間通過彼此的相互作用實現(xiàn)系統(tǒng)的行為操刀。在網(wǎng)狀結(jié)構中烁挟,幾乎每個對象都需要與其他對象發(fā)生相互作用,而這種相互作用表現(xiàn)為一個對象與另外一個對象的直接耦合骨坑,這將導致一個過度耦合的系統(tǒng)撼嗓。
中介者模式可以使對象之間的關系數(shù)量急劇減少,通過引入中介者對象,可以將系統(tǒng)的網(wǎng)狀結(jié)構變成以中介者為中心的星形結(jié)構静稻。在這個星形結(jié)構中警没,同事對象不再直接與另一個對象聯(lián)系,它通過中介者對象與另一個對象發(fā)生相互作用振湾。中介者對象的存在保證了對象結(jié)構上的穩(wěn)定杀迹,也就是說,系統(tǒng)的結(jié)構不會因為新對象的引入帶來大量的修改工作押搪。
如果在一個系統(tǒng)中對象之間存在多對多的相互關系树酪,我們可以將對象之間的一些交互行為從各個對象中分離出來,并集中封裝在一個中介者對象中大州,并由該中介者進行統(tǒng)一協(xié)調(diào)续语,這樣對象之間多對多的復雜關系就轉(zhuǎn)化為相對簡單的一對多關系。通過引入中介者來簡化對象之間的復雜交互厦画,中介者模式是“迪米特法則”的一個典型應用疮茄。
中介者模式(Mediator Pattern):用一個中介對象(中介者)來封裝一系列的對象交互,中介者使各對象不需要顯式地相互引用根暑,從而使其耦合松散力试,而且可以獨立地改變它們之間的交互。中介者模式又稱為調(diào)停者模式排嫌,它是一種對象行為型模式畸裳。
在中介者模式中,我們引入了用于協(xié)調(diào)其他對象/類之間相互調(diào)用的中介者類淳地,為了讓系統(tǒng)具有更好的靈活性和可擴展性怖糊,通常還提供了抽象中介者,其結(jié)構圖如下圖所示:

中介者模式結(jié)構圖

在中介者模式結(jié)構圖中包含如下幾個角色:
● Mediator(抽象中介者):它定義一個接口颇象,該接口用于與各同事對象之間進行通信伍伤。
● ConcreteMediator(具體中介者):它是抽象中介者的子類,通過協(xié)調(diào)各個同事對象來實現(xiàn)協(xié)作行為遣钳,它維持了對各個同事對象的引用嚷缭。
● Colleague(抽象同事類):它定義各個同事類公有的方法,并聲明了一些抽象方法來供子類實現(xiàn)耍贾,同時它維持了一個對抽象中介者類的引用,其子類可以通過該引用來與中介者通信路幸。
● ConcreteColleague(具體同事類):它是抽象同事類的子類荐开;每一個同事對象在需要和其他同事對象通信時,先與中介者通信简肴,通過中介者來間接完成與其他同事類的通信晃听;在具體同事類中實現(xiàn)了在抽象同事類中聲明的抽象方法。
中介者模式的核心在于中介者類的引入,在中介者模式中能扒,中介者類承擔了兩方面的職責:
(1) 中轉(zhuǎn)作用(結(jié)構性):通過中介者提供的中轉(zhuǎn)作用佣渴,各個同事對象就不再需要顯式引用其他同事,當需要和其他同事進行通信時初斑,可通過中介者來實現(xiàn)間接調(diào)用辛润。該中轉(zhuǎn)作用屬于中介者在結(jié)構上的支持。
(2) 協(xié)調(diào)作用(行為性):中介者可以更進一步的對同事之間的關系進行封裝见秤,同事可以一致的和中介者進行交互砂竖,而不需要指明中介者需要具體怎么做,中介者根據(jù)封裝在自身內(nèi)部的協(xié)調(diào)邏輯鹃答,對同事的請求進行進一步處理乎澄,將同事成員之間的關系行為進行分離和封裝。該協(xié)調(diào)作用屬于中介者在行為上的支持测摔。
在具體同事類ConcreteColleague中實現(xiàn)了在抽象同事類中聲明的方法置济,其中部分方法是同事類的自身方法(Self-Method),用于處理自己的行為锋八,而另外部分方式是依賴方法(Depend-Method)浙于,用于調(diào)用在中介者中定義的方法,依賴中介者來完成相應的行為查库,例如調(diào)用另一個同事類的相關方法路媚。
在中介者模式的實際使用過程中,如果需要引入新的具體同事類樊销,只需要繼承抽象同事類并實現(xiàn)其中的方法即可整慎,由于具體同事類之間并無直接的引用關系,因此原有所有同事類無須進行任何修改围苫,它們與新增同事對象之間的交互可以通過修改或者增加具體中介者類來實現(xiàn)裤园;如果需要在原有系統(tǒng)中增加新的具體中介者類,只需要繼承抽象中介者類(或已有的具體中介者類)并覆蓋其中定義的方法即可剂府,在新的具體中介者中可以通過不同的方式來處理對象之間的交互拧揽,也可以增加對新增同事的引用和調(diào)用。在客戶端中只需要修改少許代碼(如果引入配置文件的話有時可以不修改任何代碼)就可以實現(xiàn)中介者的更換腺占。
中介者模式總結(jié)
中介者模式將一個網(wǎng)狀的系統(tǒng)結(jié)構變成一個以中介者對象為中心的星形結(jié)構淤袜,在這個星型結(jié)構中,使用中介者對象與其他對象的一對多關系來取代原有對象之間的多對多關系衰伯。中介者模式在事件驅(qū)動類軟件中應用較為廣泛铡羡,特別是基于GUI(Graphical User Interface,圖形用戶界面)的應用軟件意鲸,此外烦周,在類與類之間存在錯綜復雜的關聯(lián)關系的系統(tǒng)中尽爆,中介者模式都能得到較好的應用。

  1. 主要優(yōu)點
    中介者模式的主要優(yōu)點如下:
    (1) 中介者模式簡化了對象之間的交互读慎,它用中介者和同事的一對多交互代替了原來同事之間的多對多交互漱贱,一對多關系更容易理解滋觉、維護和擴展剔蹋,將原本難以理解的網(wǎng)狀結(jié)構轉(zhuǎn)換成相對簡單的星型結(jié)構。
    (2) 中介者模式可將各同事對象解耦寞宫。中介者有利于各同事之間的松耦合闰靴,我們可以獨立的改變和復用每一個同事和中介者彪笼,增加新的中介者和新的同事類都比較方便,更好地符合“開閉原則”蚂且。
    (3) 可以減少子類生成配猫,中介者將原本分布于多個對象間的行為集中在一起,改變這些行為只需生成新的中介者子類即可杏死,這使各個同事類可被重用泵肄,無須對同事類進行擴展。
  2. 主要缺點
    中介者模式的主要缺點如下:
    在具體中介者類中包含了大量同事之間的交互細節(jié)淑翼,可能會導致具體中介者類非常復雜腐巢,使得系統(tǒng)難以維護。
  3. 適用場景
    在以下情況下可以考慮使用中介者模式:
    (1) 系統(tǒng)中對象之間存在復雜的引用關系玄括,系統(tǒng)結(jié)構混亂且難以理解冯丙。
    (2) 一個對象由于引用了其他很多對象并且直接和這些對象通信,導致難以復用該對象遭京。
    (3) 想通過一個中間類來封裝多個類中的行為胃惜,而又不想生成太多的子類∧牡瘢可以通過引入中介者類來實現(xiàn)船殉,在中介者中定義對象交互的公共行為,如果需要改變行為則可以增加新的具體中介者類斯嚎。

備忘錄模式-Memento Pattern

備忘錄模式提供了一種狀態(tài)恢復的實現(xiàn)機制利虫,使得用戶可以方便地回到一個特定的歷史步驟,當新的狀態(tài)無效或者存在問題時堡僻,可以使用暫時存儲起來的備忘錄將狀態(tài)復原糠惫,當前很多軟件都提供了撤銷(Undo)操作,其中就使用了備忘錄模式钉疫。
備忘錄模式(Memento Pattern):在不破壞封裝的前提下硼讽,捕獲一個對象的內(nèi)部狀態(tài),并在該對象之外保存這個狀態(tài)陌选,這樣可以在以后將對象恢復到原先保存的狀態(tài)理郑。它是一種對象行為型模式,其別名為Token咨油。
備忘錄模式的核心是備忘錄類以及用于管理備忘錄的負責人類的設計您炉,其結(jié)構如下圖所示:


備忘錄模式結(jié)構圖

在備忘錄模式結(jié)構圖中包含如下幾個角色:
● Originator(原發(fā)器):它是一個普通類,可以創(chuàng)建一個備忘錄役电,并存儲它的當前內(nèi)部狀態(tài)赚爵,也可以使用備忘錄來恢復其內(nèi)部狀態(tài),一般將需要保存內(nèi)部狀態(tài)的類設計為原發(fā)器法瑟。
●Memento(備忘錄):存儲原發(fā)器的內(nèi)部狀態(tài)冀膝,根據(jù)原發(fā)器來決定保存哪些內(nèi)部狀態(tài)。備忘錄的設計一般可以參考原發(fā)器的設計窝剖,根據(jù)實際需要確定備忘錄類中的屬性赐纱。需要注意的是疙描,除了原發(fā)器本身與負責人類之外,備忘錄對象不能直接供其他類使用,原發(fā)器的設計在不同的編程語言中實現(xiàn)機制會有所不同火俄。
●Caretaker(負責人):負責人又稱為管理者,它負責保存?zhèn)渫浧滓牵遣荒軐渫浀膬?nèi)容進行操作或檢查疯攒。在負責人類中可以存儲一個或多個備忘錄對象枚尼,它只負責存儲對象署恍,而不能修改對象,也無須知道對象的實現(xiàn)細節(jié)呼巷。
理解備忘錄模式并不難,但關鍵在于如何設計備忘錄類和負責人類配名。由于在備忘錄中存儲的是原發(fā)器的中間狀態(tài),因此需要防止原發(fā)器以外的其他對象訪問備忘錄芋膘,特別是不允許其他對象來修改備忘錄。
對于備忘錄類Memento而言习寸,它通常提供了與原發(fā)器相對應的屬性(可以是全部,也可以是部分)用于存儲原發(fā)器的狀態(tài)鸯匹。
在設計備忘錄類時需要考慮其封裝性,除了Originator類染厅,不允許其他類來調(diào)用備忘錄類Memento的構造函數(shù)與相關方法简逮,如果不考慮封裝性蕉堰,允許其他類調(diào)用setState()等方法冰寻,將導致在備忘錄中保存的歷史狀態(tài)發(fā)生改變乐疆,通過撤銷操作所恢復的狀態(tài)就不再是真實的歷史狀態(tài),備忘錄模式也就失去了本身的意義迷殿。
在使用Java語言實現(xiàn)備忘錄模式時诉字,一般通過將Memento類與Originator類定義在同一個包(package)中來實現(xiàn)封裝导披,在Java語言中可使用默認訪問標識符來定義Memento類墨叛,即保證其包內(nèi)可見。只有Originator類可以對Memento進行訪問卤妒,而限制了其他類對Memento的訪問洗出。在 Memento中保存了Originator的state值阱洪,如果Originator中的state值改變之后需撤銷利耍,可以通過調(diào)用它的restoreMemento()方法進行恢復魂毁。
對于負責人類Caretaker烦秩,它用于保存?zhèn)渫泴ο笕偶。⑻峁ゞetMemento()方法用于向客戶端返回一個備忘錄對象,原發(fā)器通過使用這個備忘錄對象可以回到某個歷史狀態(tài)。在Caretaker類中不應該直接調(diào)用Memento中的狀態(tài)改變方法,它的作用僅僅用于存儲備忘錄對象陶因。將原發(fā)器備份生成的備忘錄對象存儲在其中,當用戶需要對原發(fā)器進行恢復時再將存儲在其中的備忘錄對象取出贴见。
有時候用戶需要撤銷多步操作镣衡。如何實現(xiàn)多次撤銷呢惰说?那就是在負責人類中定義一個集合來存儲多個備忘錄,每個備忘錄負責保存一個歷史狀態(tài),在撤銷時可以對備忘錄集合進行逆向遍歷郁妈,回到一個指定的歷史狀態(tài)胃碾,而且還可以對備忘錄集合進行正向遍歷吁讨,實現(xiàn)重做(Redo)操作波势,即取消撤銷,讓對象狀態(tài)得到恢復逞怨。
備忘錄是一個很特殊的對象算利,只有原發(fā)器對它擁有控制的權力,負責人只負責管理风题,而其他類無法訪問到備忘錄引镊,因此我們需要對備忘錄進行封裝疹娶。
為了實現(xiàn)對備忘錄對象的封裝歧焦,需要對備忘錄的調(diào)用進行控制猖任,對于原發(fā)器而言盈滴,它可以調(diào)用備忘錄的所有信息硫朦,允許原發(fā)器訪問返回到先前狀態(tài)所需的所有數(shù)據(jù)祷舀;對于負責人而言哟忍,只負責備忘錄的保存并將備忘錄傳遞給其他對象;對于其他對象而言翘簇,只需要從負責人處取出備忘錄對象并將原發(fā)器對象的狀態(tài)恢復驼鹅,而無須關心備忘錄的保存細節(jié)。理想的情況是只允許生成該備忘錄的那個原發(fā)器訪問備忘錄的內(nèi)部狀態(tài)蛀恩。
在實際開發(fā)中寸谜,原發(fā)器與備忘錄之間的關系是非常特殊的岭埠,它們要分享信息而不讓其他類知道句喜,實現(xiàn)的方法因編程語言的不同而有所差異,在C++中可以使用friend關鍵字供搀,讓原發(fā)器類和備忘錄類成為友元類赞季,互相之間可以訪問對象的一些私有的屬性;在Java語言中可以將原發(fā)器類和備忘錄類放在一個包中狐蜕,讓它們之間滿足默認的包內(nèi)可見性贡羔,也可以將備忘錄類作為原發(fā)器類的內(nèi)部類碍讨,使得只有原發(fā)器才可以訪問備忘錄中的數(shù)據(jù)蒙秒,其他對象都無法使用備忘錄中的數(shù)據(jù)晕讲。

備忘錄模式總結(jié)
備忘錄模式在很多軟件的使用過程中普遍存在马澈,但是在應用軟件開發(fā)中弄息,它的使用頻率并不太高摹量,因為現(xiàn)在很多基于窗體和瀏覽器的應用軟件并沒有提供撤銷操作。如果需要為軟件提供撤銷功能凝果,備忘錄模式無疑是一種很好的解決方案睦尽。在一些字處理軟件、圖像編輯軟件山害、數(shù)據(jù)庫管理系統(tǒng)等軟件中備忘錄模式都得到了很好的應用沿量。
1.主要優(yōu)點
備忘錄模式的主要優(yōu)點如下:
(1)它提供了一種狀態(tài)恢復的實現(xiàn)機制朴则,使得用戶可以方便地回到一個特定的歷史步驟,當新的狀態(tài)無效或者存在問題時妖碉,可以使用暫時存儲起來的備忘錄將狀態(tài)復原芥被。
(2)備忘錄實現(xiàn)了對信息的封裝,一個備忘錄對象是一種原發(fā)器對象狀態(tài)的表示冗茸,不會被其他代碼所改動匹中。備忘錄保存了原發(fā)器的狀態(tài),采用列表挂绰、堆棧等集合來存儲備忘錄對象可以實現(xiàn)多次撤銷操作服赎。
2.主要缺點
備忘錄模式的主要缺點如下:
資源消耗過大,如果需要保存的原發(fā)器類的成員變量太多秦士,就不可避免需要占用大量的存儲空間永高,每保存一次對象的狀態(tài)都需要消耗一定的系統(tǒng)資源。
3.適用場景
在以下情況下可以考慮使用備忘錄模式:
(1)保存一個對象在某一個時刻的全部狀態(tài)或部分狀態(tài)次洼,這樣以后需要時它能夠恢復到先前的狀態(tài)遇骑,實現(xiàn)撤銷操作落萎。
(2)防止外界對象破壞一個對象歷史狀態(tài)的封裝性,避免將對象歷史狀態(tài)的實現(xiàn)細節(jié)暴露給外界對象翔脱。

觀察者模式-Observer Pattern

觀察者模式是使用頻率最高的設計模式之一媒鼓,它用于建立一種對象與對象之間的依賴關系,一個對象發(fā)生改變時將自動通知其他對象疚沐,其他對象將相應作出反應潮模。在觀察者模式中擎厢,發(fā)生改變的對象稱為觀察目標,而被通知的對象稱為觀察者芬探,一個觀察目標可以對應多個觀察者厘惦,而且這些觀察者之間可以沒有任何相互聯(lián)系,可以根據(jù)需要增加和刪除觀察者炎疆,使得系統(tǒng)更易于擴展国裳。
觀察者模式(Observer Pattern):定義對象之間的一種一對多依賴關系缝左,使得每當一個對象狀態(tài)發(fā)生改變時,其相關依賴對象皆得到通知并被自動更新蛇数。觀察者模式的別名包括發(fā)布-訂閱(Publish/Subscribe)模式是越、模型-視圖(Model/View)模式倚评、源-監(jiān)聽器(Source/Listener)模式或從屬者(Dependents)模式。觀察者模式是一種對象行為型模式盔性。
觀察者模式結(jié)構中通常包括觀察目標和觀察者兩個繼承層次結(jié)構呢岗,其結(jié)構如下圖所示:


觀察者模式結(jié)構圖

在觀察者模式結(jié)構圖中包含如下幾個角色:
● Subject(目標):目標又稱為主題后豫,它是指被觀察的對象。在目標中定義了一個觀察者集合焕襟,一個觀察目標可以接受任意數(shù)量的觀察者來觀察饭豹,它提供一系列方法來增加和刪除觀察者對象拄衰,同時它定義了通知方法notify()。目標類可以是接口茫打,也可以是抽象類或具體類。
● ConcreteSubject(具體目標):具體目標是目標類的子類轮洋,通常它包含有經(jīng)常發(fā)生改變的數(shù)據(jù)抬旺,當它的狀態(tài)發(fā)生改變時,向它的各個觀察者發(fā)出通知汉柒;同時它還實現(xiàn)了在目標類中定義的抽象業(yè)務邏輯方法(如果有的話)碾褂。如果無須擴展目標類历葛,則具體目標類可以省略。
● Observer(觀察者):觀察者將對觀察目標的改變做出反應传货,觀察者一般定義為接口问裕,該接口聲明了更新數(shù)據(jù)的方法update()孵坚,因此又稱為抽象觀察者。
● ConcreteObserver(具體觀察者):在具體觀察者中維護一個指向具體目標對象的引用巍杈,它存儲具體觀察者的有關狀態(tài)筷畦,這些狀態(tài)需要和具體目標的狀態(tài)保持一致刺洒;它實現(xiàn)了在抽象觀察者Observer中定義的update()方法。通常在實現(xiàn)時鼎文,可以調(diào)用具體目標類的attach()方法將自己添加到目標類的集合中或通過detach()方法將自己從目標類的集合中刪除因俐。
觀察者模式描述了如何建立對象與對象之間的依賴關系,以及如何構造滿足這種需求的系統(tǒng)蓉坎。觀察者模式包含觀察目標和觀察者兩類對象胡嘿,一個目標可以有任意數(shù)目的與之相依賴的觀察者,一旦觀察目標的狀態(tài)發(fā)生改變伺通,所有的觀察者都將得到通知。作為對這個通知的響應吴藻,每個觀察者都將監(jiān)視觀察目標的狀態(tài)以使其狀態(tài)與目標狀態(tài)同步沟堡,這種交互也稱為發(fā)布-訂閱(Publish-Subscribe)。觀察目標是通知的發(fā)布者禀横,它發(fā)出通知時并不需要知道誰是它的觀察者粥血,可以有任意數(shù)目的觀察者訂閱它并接收通知复亏。
在有些更加復雜的情況下,具體觀察者類ConcreteObserver的update()方法在執(zhí)行時需要使用到具體目標類ConcreteSubject中的狀態(tài)(屬性)抬闷,因此在ConcreteObserver與ConcreteSubject之間有時候還存在關聯(lián)或依賴關系笤成,在ConcreteObserver中定義一個ConcreteSubject實例眷茁,通過該實例獲取存儲在ConcreteSubject中的狀態(tài)蔼卡。如果ConcreteObserver的update()方法不需要使用到ConcreteSubject中的狀態(tài)屬性挣磨,則可以對觀察者模式的標準結(jié)構進行簡化荤懂,在具體觀察者ConcreteObserver和具體目標ConcreteSubject之間無須維持對象引用节仿。如果在具體層具有關聯(lián)關系,系統(tǒng)的擴展性將受到一定的影響矾瘾,增加新的具體目標類有時候需要修改原有觀察者的代碼壕翩,在一定程度上違反了“開閉原則”傅寡,但是如果原有觀察者類無須關聯(lián)新增的具體目標荐操,則系統(tǒng)擴展性不受影響。
觀察者模式在Java語言中的地位非常重要宅倒。在JDK的java.util包中屯耸,提供了Observable類以及Observer接口肩民,它們構成了JDK對觀察者模式的支持。如下圖所示:


JDK提供的Observable類及Observer接口結(jié)構圖

在java.util.Observer接口中只聲明一個方法灶搜,它充當抽象觀察者割卖,當觀察目標的狀態(tài)發(fā)生變化時患雏,該方法將會被調(diào)用,在Observer的子類中將實現(xiàn)update()方法丙挽,即具體觀察者可以根據(jù)需要具有不同的更新行為颜阐。當調(diào)用觀察目標類Observable的notifyObservers()方法時,將執(zhí)行觀察者類中的update()方法瑰艘。
java.util.Observable類充當觀察目標類紫新,在Observable中定義了一個向量Vector來存儲觀察者對象李剖,它所包含的方法及說明見下表:

方法名 方法描述
Observable() 構造方法杖爽,實例化Vector向量紫皇。
addObserver(Observer o) 用于注冊新的觀察者對象到向量中聪铺。
deleteObserver (Observer o) 用于刪除向量中的某一個觀察者對象。
notifyObservers()和notifyObservers(Object arg) 通知方法撒桨,用于在方法內(nèi)部循環(huán)調(diào)用向量中每一個觀察者的update()方法凤类。
deleteObservers() 用于清空向量普气,即刪除向量中所有觀察者對象。
setChanged() 該方法被調(diào)用后會設置一個boolean類型的內(nèi)部標記變量changed的值為true夷磕,表示觀察目標對象的狀態(tài)發(fā)生了變化坐桩。
clearChanged() 用于將changed變量的值設為false封锉,表示對象狀態(tài)不再發(fā)生改變或者已經(jīng)通知了所有的觀察者對象,調(diào)用了它們的update()方法萍鲸。
hasChanged() 用于測試對象狀態(tài)是否改變擦俐。
countObservers() 用于返回向量中觀察者的數(shù)量蚯瞧。

我們可以直接使用Observer接口和Observable類來作為觀察者模式的抽象層,再自定義具體觀察者類和具體觀察目標類备徐,通過使用JDK中的Observer接口和Observable類蜜猾,可以更加方便地在Java語言中應用觀察者模式振诬。

觀察者模式總結(jié)
觀察者模式是一種使用頻率非常高的設計模式赶么,無論是移動應用、Web應用或者桌面應用清钥,觀察者模式幾乎無處不在祟昭,它為實現(xiàn)對象之間的聯(lián)動提供了一套完整的解決方案怖侦,凡是涉及到一對一或者一對多的對象交互場景都可以使用觀察者模式础钠。觀察者模式廣泛應用于各種編程語言的GUI事件處理的實現(xiàn)旗吁,在基于事件的XML解析技術(如SAX2)以及Web事件處理中也都使用了觀察者模式。
1.主要優(yōu)點
觀察者模式的主要優(yōu)點如下:
(1) 觀察者模式可以實現(xiàn)表示層和數(shù)據(jù)邏輯層的分離香府,定義了穩(wěn)定的消息更新傳遞機制企孩,并抽象了更新接口,使得可以有各種各樣不同的表示層充當具體觀察者角色擒抛。
(2) 觀察者模式在觀察目標和觀察者之間建立一個抽象的耦合歧沪。觀察目標只需要維持一個抽象觀察者的集合莲组,無須了解其具體觀察者锹杈。由于觀察目標和觀察者沒有緊密地耦合在一起,因此它們可以屬于不同的抽象化層次邪码。
(3) 觀察者模式支持廣播通信霞扬,觀察目標會向所有已注冊的觀察者對象發(fā)送通知枫振,簡化了一對多系統(tǒng)設計的難度。
(4) 觀察者模式滿足“開閉原則”的要求萤彩,增加新的具體觀察者無須修改原有系統(tǒng)代碼粪滤,在具體觀察者與觀察目標之間不存在關聯(lián)關系的情況下,增加新的觀察目標也很方便雀扶。
2.主要缺點
觀察者模式的主要缺點如下:
(1) 如果一個觀察目標對象有很多直接和間接觀察者杖小,將所有的觀察者都通知到會花費很多時間。
(2) 如果在觀察者和觀察目標之間存在循環(huán)依賴愚墓,觀察目標會觸發(fā)它們之間進行循環(huán)調(diào)用予权,可能導致系統(tǒng)崩潰浪册。
(3) 觀察者模式?jīng)]有相應的機制讓觀察者知道所觀察的目標對象是怎么發(fā)生變化的扫腺,而僅僅只是知道觀察目標發(fā)生了變化。
3.適用場景
在以下情況下可以考慮使用觀察者模式:
(1) 一個抽象模型有兩個方面村象,其中一個方面依賴于另一個方面笆环,將這兩個方面封裝在獨立的對象中使它們可以各自獨立地改變和復用攒至。
(2) 一個對象的改變將導致一個或多個其他對象也發(fā)生改變,而并不知道具體有多少對象將發(fā)生改變躁劣,也不知道這些對象是誰迫吐。
(3) 需要在系統(tǒng)中創(chuàng)建一個觸發(fā)鏈,A對象的行為將影響B(tài)對象账忘,B對象的行為將影響C對象……志膀,可以使用觀察者模式創(chuàng)建一種鏈式觸發(fā)機制。

狀態(tài)模式-State Pattern

狀態(tài)模式用于解決系統(tǒng)中復雜對象的狀態(tài)轉(zhuǎn)換以及不同狀態(tài)下行為的封裝問題鳖擒。當系統(tǒng)中某個對象存在多個狀態(tài)梧却,這些狀態(tài)之間可以進行轉(zhuǎn)換,而且對象在不同狀態(tài)下行為不相同時可以使用狀態(tài)模式败去。狀態(tài)模式將一個對象的狀態(tài)從該對象中分離出來放航,封裝到專門的狀態(tài)類中,使得對象狀態(tài)可以靈活變化圆裕,對于客戶端而言广鳍,無須關心對象狀態(tài)的轉(zhuǎn)換以及對象所處的當前狀態(tài),無論對于何種狀態(tài)的對象吓妆,客戶端都可以一致處理赊时。
狀態(tài)模式(State Pattern):允許一個對象在其內(nèi)部狀態(tài)改變時改變它的行為,對象看起來似乎修改了它的類行拢。其別名為狀態(tài)對象(Objects for States)祖秒,狀態(tài)模式是一種對象行為型模式。
在狀態(tài)模式中引入了抽象狀態(tài)類和具體狀態(tài)類舟奠,它們是狀態(tài)模式的核心竭缝,其結(jié)構如下圖所示:


狀態(tài)模式結(jié)構圖

在狀態(tài)模式結(jié)構圖中包含如下幾個角色:
● Context(環(huán)境類):環(huán)境類又稱為上下文類,它是擁有多種狀態(tài)的對象沼瘫。由于環(huán)境類的狀態(tài)存在多樣性且在不同狀態(tài)下對象的行為有所不同抬纸,因此將狀態(tài)獨立出去形成單獨的狀態(tài)類。在環(huán)境類中維護一個抽象狀態(tài)類State的實例耿戚,這個實例定義當前狀態(tài)湿故,在具體實現(xiàn)時,它是一個State子類的對象膜蛔。
● State(抽象狀態(tài)類):它用于定義一個接口以封裝與環(huán)境類的一個特定狀態(tài)相關的行為坛猪,在抽象狀態(tài)類中聲明了各種不同狀態(tài)對應的方法,而在其子類中實現(xiàn)類這些方法皂股,由于不同狀態(tài)下對象的行為可能不同墅茉,因此在不同子類中方法的實現(xiàn)可能存在不同,相同的方法可以寫在抽象狀態(tài)類中。
● ConcreteState(具體狀態(tài)類):它是抽象狀態(tài)類的子類躁锁,每一個子類實現(xiàn)一個與環(huán)境類的一個狀態(tài)相關的行為纷铣,每一個具體狀態(tài)類對應環(huán)境的一個具體狀態(tài),不同的具體狀態(tài)類其行為有所不同战转。
在狀態(tài)模式的使用過程中搜立,一個對象的狀態(tài)之間還可以進行相互轉(zhuǎn)換糠悼,通常有兩種實現(xiàn)狀態(tài)轉(zhuǎn)換的方式:
(1) 統(tǒng)一由環(huán)境類來負責狀態(tài)之間的轉(zhuǎn)換官觅,此時律胀,環(huán)境類還充當了狀態(tài)管理器(State Manager)角色闹蒜,在環(huán)境類的業(yè)務方法中通過對某些屬性值的判斷實現(xiàn)狀態(tài)轉(zhuǎn)換,還可以提供一個專門的方法用于實現(xiàn)屬性判斷和狀態(tài)轉(zhuǎn)換素标。
(2) 由具體狀態(tài)類來負責狀態(tài)之間的轉(zhuǎn)換险污,可以在具體狀態(tài)類的業(yè)務方法中判斷環(huán)境類的某些屬性值再根據(jù)情況為環(huán)境類設置新的狀態(tài)對象致讥,實現(xiàn)狀態(tài)轉(zhuǎn)換膀懈,同樣顿锰,也可以提供一個專門的方法來負責屬性值的判斷和狀態(tài)轉(zhuǎn)換。此時启搂,狀態(tài)類與環(huán)境類之間就將存在依賴或關聯(lián)關系硼控,因為狀態(tài)類需要訪問環(huán)境類中的屬性值,這時在狀態(tài)模式結(jié)構圖中就有一條State指向Context的組合線胳赌。

在有些情況下牢撼,多個環(huán)境對象可能需要共享同一個狀態(tài),如果希望在系統(tǒng)中實現(xiàn)多個環(huán)境對象共享一個或多個狀態(tài)對象疑苫,那么需要將這些狀態(tài)對象定義為環(huán)境類的靜態(tài)成員對象熏版。

狀態(tài)模式總結(jié)
狀態(tài)模式將一個對象在不同狀態(tài)下的不同行為封裝在一個個狀態(tài)類中,通過設置不同的狀態(tài)對象可以讓環(huán)境對象擁有不同的行為捍掺,而狀態(tài)轉(zhuǎn)換的細節(jié)對于客戶端而言是透明的撼短,方便了客戶端的使用。在實際開發(fā)中乡小,狀態(tài)模式具有較高的使用頻率阔加,在工作流和游戲開發(fā)中狀態(tài)模式都得到了廣泛的應用,例如公文狀態(tài)的轉(zhuǎn)換满钟、游戲中角色的升級等。

  1. 主要優(yōu)點
    狀態(tài)模式的主要優(yōu)點如下:
    (1) 封裝了狀態(tài)的轉(zhuǎn)換規(guī)則胳喷,在狀態(tài)模式中可以將狀態(tài)的轉(zhuǎn)換代碼封裝在環(huán)境類或者具體狀態(tài)類中湃番,可以對狀態(tài)轉(zhuǎn)換代碼進行集中管理,而不是分散在一個個業(yè)務方法中吭露。
    (2) 將所有與某個狀態(tài)有關的行為放到一個類中吠撮,只需要注入一個不同的狀態(tài)對象即可使環(huán)境對象擁有不同的行為。
    (3) 允許狀態(tài)轉(zhuǎn)換邏輯與狀態(tài)對象合成一體讲竿,而不是提供一個巨大的條件語句塊泥兰,狀態(tài)模式可以讓我們避免使用龐大的條件語句來將業(yè)務方法和狀態(tài)轉(zhuǎn)換代碼交織在一起弄屡。
    (4) 可以讓多個環(huán)境對象共享一個狀態(tài)對象,從而減少系統(tǒng)中對象的個數(shù)鞋诗。
  2. 主要缺點
    狀態(tài)模式的主要缺點如下:
    (1) 狀態(tài)模式的使用必然會增加系統(tǒng)中類和對象的個數(shù)膀捷,導致系統(tǒng)運行開銷增大。
    (2) 狀態(tài)模式的結(jié)構與實現(xiàn)都較為復雜削彬,如果使用不當將導致程序結(jié)構和代碼的混亂全庸,增加系統(tǒng)設計的難度。
    (3) 狀態(tài)模式對“開閉原則”的支持并不太好融痛,增加新的狀態(tài)類需要修改那些負責狀態(tài)轉(zhuǎn)換的源代碼壶笼,否則無法轉(zhuǎn)換到新增狀態(tài);而且修改某個狀態(tài)類的行為也需修改對應類的源代碼雁刷。
  3. 適用場景
    在以下情況下可以考慮使用狀態(tài)模式:
    (1) 對象的行為依賴于它的狀態(tài)(如某些屬性值)覆劈,狀態(tài)的改變將導致行為的變化。
    (2) 在代碼中包含大量與對象狀態(tài)有關的條件語句沛励,這些條件語句的出現(xiàn)墩崩,會導致代碼的可維護性和靈活性變差,不能方便地增加和刪除狀態(tài)侯勉,并且導致客戶類與類庫之間的耦合增強鹦筹。

策略模式-Strategy Pattern

策略模式(Strategy Pattern):定義一系列算法類,將每一個算法封裝起來址貌,并讓它們可以相互替換铐拐,策略模式讓算法獨立于使用它的客戶而變化,也稱為政策模式(Policy)练对。策略模式是一種對象行為型模式遍蟋。
策略模式的主要目的是將算法的定義與使用分開,也就是將算法的行為和環(huán)境分開螟凭,將算法的定義放在專門的策略類中虚青,每一個策略類封裝了一種實現(xiàn)算法,使用算法的環(huán)境類針對抽象策略類進行編程螺男,符合“依賴倒轉(zhuǎn)原則”棒厘。在出現(xiàn)新的算法時,只需要增加一個新的實現(xiàn)了抽象策略類的具體策略類即可下隧。策略模式定義如下:
策略模式結(jié)構并不復雜奢人,但我們需要理解其中環(huán)境類Context的作用,其結(jié)構如下圖所示:


策略模式結(jié)構圖

在策略模式結(jié)構圖中包含如下幾個角色:
● Context(環(huán)境類):環(huán)境類是使用算法的角色淆院,它在解決某個問題(即實現(xiàn)某個方法)時可以采用多種策略何乎。在環(huán)境類中維持一個對抽象策略類的引用實例,用于定義所采用的策略。
● Strategy(抽象策略類):它為所支持的算法聲明了抽象方法支救,是所有策略類的父類抢野,它可以是抽象類或具體類,也可以是接口各墨。環(huán)境類通過抽象策略類中聲明的方法在運行時調(diào)用具體策略類中實現(xiàn)的算法指孤。
● ConcreteStrategy(具體策略類):它實現(xiàn)了在抽象策略類中聲明的算法,在運行時欲主,具體策略類將覆蓋在環(huán)境類中定義的抽象策略類對象邓厕,使用一種具體的算法實現(xiàn)某個業(yè)務處理。

策略模式總結(jié)
策略模式用于算法的自由切換和擴展扁瓢,它是應用較為廣泛的設計模式之一详恼。策略模式對應于解決某一問題的一個算法族,允許用戶從該算法族中任選一個算法來解決某一問題引几,同時可以方便地更換算法或者增加新的算法昧互。只要涉及到算法的封裝、復用和切換都可以考慮使用策略模式伟桅。

  1. 主要優(yōu)點
    策略模式的主要優(yōu)點如下:
    (1) 策略模式提供了對“開閉原則”的完美支持敞掘,用戶可以在不修改原有系統(tǒng)的基礎上選擇算法或行為,也可以靈活地增加新的算法或行為楣铁。
    (2) 策略模式提供了管理相關的算法族的辦法玖雁。策略類的等級結(jié)構定義了一個算法或行為族,恰當使用繼承可以把公共的代碼移到抽象策略類中盖腕,從而避免重復的代碼赫冬。
    (3) 策略模式提供了一種可以替換繼承關系的辦法。如果不使用策略模式溃列,那么使用算法的環(huán)境類就可能會有一些子類劲厌,每一個子類提供一種不同的算法。但是听隐,這樣一來算法的使用就和算法本身混在一起补鼻,不符合“單一職責原則”,決定使用哪一種算法的邏輯和該算法本身混合在一起雅任,從而不可能再獨立演化风范;而且使用繼承無法實現(xiàn)算法或行為在程序運行時的動態(tài)切換。
    (4) 使用策略模式可以避免多重條件選擇語句椿访。多重條件選擇語句不易維護乌企,它把采取哪一種算法或行為的邏輯與算法或行為本身的實現(xiàn)邏輯混合在一起成玫,將它們?nèi)坑簿幋a(Hard Coding)在一個龐大的多重條件選擇語句中,比直接繼承環(huán)境類的辦法還要原始和落后。
    (5) 策略模式提供了一種算法的復用機制哭当,由于將算法單獨提取出來封裝在策略類中猪腕,因此不同的環(huán)境類可以方便地復用這些策略類。
  2. 主要缺點
    策略模式的主要缺點如下:
    (1) 客戶端必須知道所有的策略類钦勘,并自行決定使用哪一個策略類陋葡。這就意味著客戶端必須理解這些算法的區(qū)別,以便適時選擇恰當?shù)乃惴ǔ共伞Q言之腐缤,策略模式只適用于客戶端知道所有的算法或行為的情況。
    (2) 策略模式將造成系統(tǒng)產(chǎn)生很多具體策略類肛响,任何細小的變化都將導致系統(tǒng)要增加一個新的具體策略類岭粤。
    (3) 無法同時在客戶端使用多個策略類,也就是說特笋,在使用策略模式時剃浇,客戶端每次只能使用一個策略類,不支持使用一個策略類完成部分功能后再使用另一個策略類來完成剩余功能的情況猎物。
  3. 適用場景
    在以下情況下可以考慮使用策略模式:
    (1) 一個系統(tǒng)需要動態(tài)地在幾種算法中選擇一種虎囚,那么可以將這些算法封裝到一個個的具體算法類中,而這些具體算法類都是一個抽象算法類的子類蔫磨。換言之淘讥,這些具體算法類均有統(tǒng)一的接口,根據(jù)“里氏代換原則”和面向?qū)ο蟮亩鄳B(tài)性堤如,客戶端可以選擇使用任何一個具體算法類蒲列,并只需要維持一個數(shù)據(jù)類型是抽象算法類的對象。
    (2) 一個對象有很多的行為煤惩,如果不用恰當?shù)哪J郊掂郑@些行為就只好使用多重條件選擇語句來實現(xiàn)。此時魄揉,使用策略模式剪侮,把這些行為轉(zhuǎn)移到相應的具體策略類里面,就可以避免使用難以維護的多重條件選擇語句洛退。
    (3) 不希望客戶端知道復雜的瓣俯、與算法相關的數(shù)據(jù)結(jié)構,在具體策略類中封裝算法與相關的數(shù)據(jù)結(jié)構兵怯,可以提高算法的保密性與安全性彩匕。

模板方法模式-Template Method Pattern

模板方法模式:定義一個操作中算法的框架,而將一些步驟延遲到子類中媒区。模板方法模式使得子類可以不改變一個算法的結(jié)構即可重定義該算法的某些特定步驟驼仪。
模板方法模式是一種基于繼承的代碼復用技術掸犬,它是一種類行為型模式。模板方法模式是結(jié)構最簡單的行為型設計模式绪爸,在其結(jié)構中只存在父類與子類之間的繼承關系湾碎。通過使用模板方法模式,可以將一些復雜流程的實現(xiàn)步驟封裝在一系列基本方法中奠货,在抽象父類中提供一個稱之為模板方法的方法來定義這些基本方法的執(zhí)行次序介褥,而通過其子類來覆蓋某些步驟,從而使得相同的算法框架可以有不同的執(zhí)行結(jié)果递惋。模板方法模式提供了一個模板方法來定義算法框架柔滔,而某些具體步驟的實現(xiàn)可以在其子類中完成。
模板方法模式結(jié)構比較簡單萍虽,其核心是抽象類和其中的模板方法的設計睛廊,其結(jié)構如下圖所示:


模板方法模式結(jié)構圖

模板方法模式包含如下兩個角色:
(1) AbstractClass(抽象類):在抽象類中定義了一系列基本操作(PrimitiveOperations),這些基本操作可以是具體的贩挣,也可以是抽象的喉前,每一個基本操作對應算法的一個步驟,在其子類中可以重定義或?qū)崿F(xiàn)這些步驟王财。同時卵迂,在抽象類中實現(xiàn)了一個模板方法(Template Method),用于定義一個算法的框架绒净,模板方法不僅可以調(diào)用在抽象類中實現(xiàn)的基本方法见咒,也可以調(diào)用在抽象類的子類中實現(xiàn)的基本方法,還可以調(diào)用其他對象中的方法挂疆。
(2) ConcreteClass(具體子類):它是抽象類的子類改览,用于實現(xiàn)在父類中聲明的抽象基本操作以完成子類特定算法的步驟,也可以覆蓋在父類中已經(jīng)實現(xiàn)的具體基本操作缤言。

在實現(xiàn)模板方法模式時宝当,開發(fā)抽象類的軟件設計師和開發(fā)具體子類的軟件設計師之間可以進行協(xié)作。一個設計師負責給出一個算法的輪廓和框架胆萧,另一些設計師則負責給出這個算法的各個邏輯步驟庆揩。實現(xiàn)這些具體邏輯步驟的方法即為基本方法,而將這些基本方法匯總起來的方法即為模板方法跌穗,模板方法模式的名字也因此而來订晌。下面將詳細介紹模板方法和基本方法:

  1. 模板方法
    一個模板方法是定義在抽象類中的、把基本操作方法組合在一起形成一個總算法或一個總行為的方法蚌吸。這個模板方法定義在抽象類中锈拨,并由子類不加以修改地完全繼承下來。模板方法是一個具體方法羹唠,它給出了一個頂層邏輯框架奕枢,而邏輯的組成步驟在抽象類中可以是具體方法娄昆,也可以是抽象方法。由于模板方法是具體方法验辞,因此模板方法模式中的抽象層只能是抽象類稿黄,而不是接口喊衫。
  2. 基本方法
    基本方法是實現(xiàn)算法各個步驟的方法跌造,是模板方法的組成部分∽骞海基本方法又可以分為三種:抽象方法(Abstract Method)壳贪、具體方法(Concrete Method)和鉤子方法(Hook Method)。
  • 抽象方法:一個抽象方法由抽象類聲明寝杖、由其具體子類實現(xiàn)违施。在C#語言里一個抽象方法以abstract關鍵字標識。
  • 具體方法:一個具體方法由一個抽象類或具體類聲明并實現(xiàn)瑟幕,其子類可以進行覆蓋也可以直接繼承磕蒲。
  • 鉤子方法:一個鉤子方法由一個抽象類或具體類聲明并實現(xiàn),而其子類可能會加以擴展只盹。模板方法模式中辣往,在父類中提供了一個定義算法框架的模板方法,還提供了一系列抽象方法殖卑、具體方法和鉤子方法站削,其中鉤子方法的引入使得子類可以控制父類的行為。鉤子方法有兩類:第一類鉤子方法可以與一些具體步驟“掛鉤”孵稽,以實現(xiàn)在不同條件下執(zhí)行模板方法中的不同步驟许起,這類鉤子方法的返回類型通常是bool類型的,這類方法名一般為IsXXX()菩鲜,用于對某個條件進行判斷园细,如果條件滿足則執(zhí)行某一步驟,否則將不執(zhí)行接校;還有一類鉤子方法就是實現(xiàn)體為空的具體方法猛频,子類可以根據(jù)需要覆蓋或者繼承這些鉤子方法,與抽象方法相比馅笙,這類鉤子方法的好處在于子類如果沒有覆蓋父類中定義的鉤子方法伦乔,編譯可以正常通過,但是如果沒有覆蓋父類中聲明的抽象方法董习,編譯將報錯烈和。

在模板方法模式中,由于面向?qū)ο蟮亩鄳B(tài)性皿淋,子類對象在運行時將覆蓋父類對象招刹,子類中定義的方法也將覆蓋父類中定義的方法恬试,因此程序在運行時,具體子類的基本方法將覆蓋父類中定義的基本方法疯暑,子類的鉤子方法也將覆蓋父類的鉤子方法训柴,從而可以通過在子類中實現(xiàn)的鉤子方法對父類方法的執(zhí)行進行約束,實現(xiàn)子類對父類行為的反向控制妇拯。

模板方法模式總結(jié)
模板方法模式是基于繼承的代碼復用技術幻馁,它體現(xiàn)了面向?qū)ο蟮闹T多重要思想,是一種使用較為頻繁的模式越锈。模板方法模式廣泛應用于框架設計中仗嗦,以確保通過父類來控制處理流程的邏輯順序(如框架的初始化,測試流程的設置等)甘凭。
1.優(yōu)點
模板方法模式的主要優(yōu)點如下:
(1) 在父類中形式化地定義一個算法稀拐,而由它的子類來實現(xiàn)細節(jié)的處理,在子類實現(xiàn)詳細的處理算法時并不會改變算法中步驟的執(zhí)行次序丹弱。
(2) 模板方法模式是一種代碼復用技術德撬,它在類庫設計中尤為重要,它提取了類庫中的公共行為躲胳,將公共行為放在父類中蜓洪,而通過其子類來實現(xiàn)不同的行為,它鼓勵我們恰當使用繼承來實現(xiàn)代碼復用泛鸟。
(3) 可實現(xiàn)一種反向控制結(jié)構蝠咆,通過子類覆蓋父類的鉤子方法來決定某一特定步驟是否需要執(zhí)行。
(4) 在模板方法模式中可以通過子類來覆蓋父類的基本方法北滥,不同的子類可以提供基本方法的不同實現(xiàn)刚操,更換和增加新的子類很方便,符合單一職責原則和開閉原則再芋。
2.模式缺點
模板方法模式的主要缺點如下:
需要為每一個基本方法的不同實現(xiàn)提供一個子類菊霜,如果父類中可變的基本方法太多,將會導致類的個數(shù)增加济赎,系統(tǒng)更加龐大鉴逞,設計也更加抽象,此時司训,可結(jié)合橋接模式來進行設計构捡。

  1. 模式適用場景
    在以下情況下可以考慮使用模板方法模式:
    (1) 對一些復雜的算法進行分割,將其算法中固定不變的部分設計為模板方法和父類具體方法壳猜,而一些可以改變的細節(jié)由其子類來實現(xiàn)勾徽。即:一次性實現(xiàn)一個算法的不變部分,并將可變的行為留給子類來實現(xiàn)统扳。
    (2) 各子類中公共的行為應被提取出來并集中到一個公共父類中以避免代碼重復喘帚。
    (3) 需要通過子類來決定父類算法中某個步驟是否執(zhí)行畅姊,實現(xiàn)子類對父類的反向控制。

訪問者模式-Visitor Pattern

如何為同一集合對象中的元素提供多種不同的操作方式吹由?訪問者模式就是一個值得考慮的解決方案若未,它可以在一定程度上解決上述問題(解決大部分問題)。訪問者模式可以為為不同類型的元素提供多種訪問操作方式倾鲫,而且可以在不修改原有系統(tǒng)的情況下增加新的操作方式粗合。
訪問者模式是一種較為復雜的行為型設計模式,它包含訪問者和被訪問元素兩個主要組成部分级乍,這些被訪問的元素通常具有不同的類型舌劳,且不同的訪問者可以對它們進行不同的訪問操作。例如處方單中的各種藥品信息就是被訪問的元素玫荣,而劃價人員和藥房工作人員就是訪問者。訪問者模式使得用戶可以在不修改現(xiàn)有系統(tǒng)的情況下擴展系統(tǒng)的功能大诸,為這些不同類型的元素增加新的操作捅厂。
在使用訪問者模式時,被訪問元素通常不是單獨存在的资柔,它們存儲在一個集合中焙贷,這個集合被稱為“對象結(jié)構”,訪問者通過遍歷對象結(jié)構實現(xiàn)對其中存儲的元素的逐個操作贿堰。
訪問者模式的結(jié)構較為復雜辙芍,其結(jié)構如下圖所示:


訪問者模式的結(jié)構圖

在訪問者模式結(jié)構圖中包含如下幾個角色:
●Vistor(抽象訪問者):抽象訪問者為對象結(jié)構中每一個具體元素類ConcreteElement聲明一個訪問操作,從這個操作的名稱或參數(shù)類型可以清楚知道需要訪問的具體元素的類型羹与,具體訪問者需要實現(xiàn)這些操作方法故硅,定義對這些元素的訪問操作。
●ConcreteVisitor(具體訪問者):具體訪問者實現(xiàn)了每個由抽象訪問者聲明的操作纵搁,每一個操作用于訪問對象結(jié)構中一種類型的元素吃衅。
●Element(抽象元素):抽象元素一般是抽象類或者接口,它定義一個accept()方法腾誉,該方法通常以一個抽象訪問者作為參數(shù)徘层。【稍后將介紹為什么要這樣設計利职∪ばВ】
●ConcreteElement(具體元素):具體元素實現(xiàn)了accept()方法,在accept()方法中調(diào)用訪問者的訪問方法以便完成對一個元素的操作猪贪。
● ObjectStructure(對象結(jié)構):對象結(jié)構是一個元素的集合跷敬,它用于存放元素對象,并且提供了遍歷其內(nèi)部元素的方法哮伟。它可以結(jié)合組合模式來實現(xiàn)干花,也可以是一個簡單的集合對象妄帘,如一個List對象或一個Set對象。

訪問者模式中對象結(jié)構存儲了不同類型的元素對象池凄,以供不同訪問者訪問抡驼。訪問者模式包括兩個層次結(jié)構,一個是訪問者層次結(jié)構肿仑,提供了抽象訪問者和具體訪問者致盟,一個是元素層次結(jié)構,提供了抽象元素和具體元素尤慰。相同的訪問者可以以不同的方式訪問不同的元素馏锡,相同的元素可以接受不同訪問者以不同訪問方式訪問。在訪問者模式中伟端,增加新的訪問者無須修改原有系統(tǒng)杯道,系統(tǒng)具有較好的可擴展性。
在訪問者模式中责蝠,抽象訪問者定義了訪問元素對象的方法党巾,通常為每一種類型的元素對象都提供一個訪問方法,而具體訪問者可以實現(xiàn)這些訪問方法霜医。這些訪問方法的命名一般有兩種方式:一種是直接在方法名中標明待訪問元素對象的具體類型齿拂,如visitElementA(ElementA elementA),還有一種是統(tǒng)一取名為visit()肴敛,通過參數(shù)類型的不同來定義一系列重載的visit()方法署海。當然,如果所有的訪問者對某一類型的元素的訪問操作都相同医男,則可以將操作代碼移到抽象訪問者類中砸狞。

通過調(diào)用Visitor類的visit()方法實現(xiàn)對元素的訪問,并以當前對象作為visit()方法的參數(shù)昨登。其具體執(zhí)行過程如下:
(1) 調(diào)用具體元素類的accept(Visitor visitor)方法趾代,并將Visitor子類對象作為其參數(shù);
(2) 在具體元素類accept(Visitor visitor)方法內(nèi)部調(diào)用傳入的Visitor對象的visit()方法丰辣,如visit(ConcreteElementA elementA)撒强,將當前具體元素類對象(this)作為參數(shù),如visitor.visit(this)笙什;
(3) 執(zhí)行Visitor對象的visit()方法飘哨,在其中還可以調(diào)用具體元素對象的業(yè)務方法。

這種調(diào)用機制也稱為“雙重分派”琐凭,正因為使用了雙重分派機制芽隆,使得增加新的訪問者無須修改現(xiàn)有類庫代碼,只需將新的訪問者對象作為參數(shù)傳入具體元素對象的accept()方法,程序運行時將回調(diào)在新增Visitor類中定義的visit()方法胚吁,從而增加新的元素訪問方式牙躺。

訪問者模式與組合模式聯(lián)用
在訪問者模式中,包含一個用于存儲元素對象集合的對象結(jié)構腕扶,我們通衬蹩剑可以使用迭代器來遍歷對象結(jié)構,同時具體元素之間可以存在整體與部分關系半抱,有些元素作為容器對象脓恕,有些元素作為成員對象,可以使用組合模式來組織元素窿侈。引入組合模式后的訪問者模式結(jié)構圖如下圖所示:

引入組合模式后的訪問者模式結(jié)構圖

由于葉子元素的遍歷操作已經(jīng)在容器元素中完成炼幔,因此要防止單獨將已增加到容器元素中的葉子元素再次加入對象結(jié)構中,對象結(jié)構中只保存容器元素和孤立的葉子元素史简。

訪問者模式總結(jié)
由于訪問者模式的使用條件較為苛刻乃秀,本身結(jié)構也較為復雜,因此在實際應用中使用頻率不是特別高乘瓤。當系統(tǒng)中存在一個較為復雜的對象結(jié)構环形,且不同訪問者對其所采取的操作也不相同時,可以考慮使用訪問者模式進行設計衙傀。在XML文檔解析萨咕、編譯器的設計、復雜集合對象的處理等領域訪問者模式得到了一定的應用聪建。
1.主要優(yōu)點
訪問者模式的主要優(yōu)點如下:
(1) 增加新的訪問操作很方便。使用訪問者模式茫陆,增加新的訪問操作就意味著增加一個新的具體訪問者類簿盅,實現(xiàn)簡單挥下,無須修改源代碼,符合“開閉原則”桨醋。
(2) 將有關元素對象的訪問行為集中到一個訪問者對象中喜最,而不是分散在一個個的元素類中。類的職責更加清晰限书,有利于對象結(jié)構中元素對象的復用章咧,相同的對象結(jié)構可以供多個不同的訪問者訪問慧邮。
(3) 讓用戶能夠在不修改現(xiàn)有元素類層次結(jié)構的情況下误澳,定義作用于該層次結(jié)構的操作忆谓。
2.主要缺點
訪問者模式的主要缺點如下:
(1) 增加新的元素類很困難倡缠。在訪問者模式中,每增加一個新的元素類都意味著要在抽象訪問者角色中增加一個新的抽象操作昙沦,并在每一個具體訪問者類中增加相應的具體操作盾饮,這違背了“開閉原則”的要求丘损。
(2) 破壞封裝徘钥。訪問者模式要求訪問者對象訪問并調(diào)用每一個元素對象的操作呈础,這意味著元素對象有時候必須暴露一些自己的內(nèi)部操作和內(nèi)部狀態(tài)猪落,否則無法供訪問者訪問笨忌。
3.適用場景
在以下情況下可以考慮使用訪問者模式:
(1) 一個對象結(jié)構包含多個類型的對象辐怕,希望對這些對象實施一些依賴其具體類型的操作垢夹。在訪問者中針對每一種具體的類型都提供了一個訪問操作果元,不同類型的對象可以有不同的訪問操作而晒。
(2) 需要對一個對象結(jié)構中的對象進行很多不同的并且不相關的操作倡怎,而需要避免讓這些操作“污染”這些對象的類监署,也不希望在增加新操作時修改這些類钠乏。訪問者模式使得我們可以將相關的訪問操作集中起來定義在訪問者類中晓避,對象結(jié)構可以被多個不同的訪問者類所使用够滑,將對象本身與對象的訪問操作分離彰触。
(3) 對象結(jié)構中對象對應的類很少改變况毅,但經(jīng)常需要在此對象結(jié)構上定義新的操作尔许。

參考

http://www.reibang.com/p/73898ff0fcde

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市余佛,隨后出現(xiàn)的幾起案子辉巡,更是在濱河造成了極大的恐慌郊楣,老刑警劉巖净蚤,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件塞栅,死亡現(xiàn)場離奇詭異放椰,居然都是意外死亡砾医,警方通過查閱死者的電腦和手機如蚜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門错邦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來撬呢,“玉大人魂拦,你說我怎么就攤上這事芯勘『摄担” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長慨绳。 經(jīng)常有香客問我脐雪,道長战秋,這世上最難降的妖魔是什么脂信? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任狰闪,我火速辦了婚禮埋泵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘雁社。我一直安慰自己歧胁,他們只是感情好,可當我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布箍鼓。 她就那樣靜靜地躺著何暮,像睡著了一般海洼。 火紅的嫁衣襯著肌膚如雪坏逢。 梳的紋絲不亂的頭發(fā)上是整,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天浮入,我揣著相機與錄音,去河邊找鬼易迹。 笑死赴蝇,一個胖子當著我的面吹牛句伶,可吹牛的內(nèi)容都是我干的考余。 我是一名探鬼主播楚堤,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼酥筝!你這毒婦竟也來了嘿歌?” 一聲冷哼從身側(cè)響起宙帝,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤愿待,失蹤者是張志新(化名)和其女友劉穎呼盆,沒想到半個月后访圃,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體腿时,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡批糟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了否淤。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片石抡。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖隐解,靈堂內(nèi)的尸體忽然破棺而出厢漩,到底是詐尸還是另有隱情溜嗜,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布土全,位于F島的核電站裹匙,受9級特大地震影響概页,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜项鬼,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一绘盟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧稚虎,春花似錦蠢终、人聲如沸寻拂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽垮卓。三九已至诬滩,卻和暖如春疼鸟,著一層夾襖步出監(jiān)牢的瞬間空镜,已是汗流浹背姑裂。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留茴厉,地道東北人矾缓。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像琉雳,于是被迫代替她去往敵國和親翠肘。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,086評論 2 355