設(shè)計(jì)模式 - 行為型模式

設(shè)計(jì)模式-面相對(duì)象5個(gè)原則
設(shè)計(jì)模式-創(chuàng)造性模式 單例 原型 工廠 建造者
設(shè)計(jì)模式-設(shè)計(jì)模式 - 結(jié)構(gòu)性模式 代理 適配器 橋接 裝飾 外觀 享元 組合
設(shè)計(jì)模式-行為性模式

行為型模式用于描述程序在運(yùn)行時(shí)復(fù)雜的流程控制,即描述多個(gè)類或?qū)ο笾g怎樣相互協(xié)作共同完成單個(gè)對(duì)象都無(wú)法單獨(dú)完成的任務(wù),它涉及算法與對(duì)象間職責(zé)的分配。

行為型模式分為類行為模式和對(duì)象行為模式,前者采用繼承機(jī)制來(lái)在類間分派行為,后者采用組合或聚合在對(duì)象間分配行為。由于組合關(guān)系或聚合關(guān)系比繼承關(guān)系耦合度低,滿足“合成復(fù)用原則”,所以對(duì)象行為模式比類行為模式具有更大的靈活性。

行為型模式是 GoF 設(shè)計(jì)模式中最為龐大的一類,它包含以下 11 種模式。

  1. 模板方法(Template Method)模式:定義一個(gè)操作中的算法骨架,將算法的一些步驟延遲到子類中,使得子類在可以不改變?cè)撍惴ńY(jié)構(gòu)的情況下重定義該算法的某些特定步驟。
  2. 策略(Strategy)模式:定義了一系列算法,并將每個(gè)算法封裝起來(lái),使它們可以相互替換,且算法的改變不會(huì)影響使用算法的客戶。
  3. 命令(Command)模式:將一個(gè)請(qǐng)求封裝為一個(gè)對(duì)象,使發(fā)出請(qǐng)求的責(zé)任和執(zhí)行請(qǐng)求的責(zé)任分割開(kāi)。
  4. 職責(zé)鏈(Chain of Responsibility)模式:把請(qǐng)求從鏈中的一個(gè)對(duì)象傳到下一個(gè)對(duì)象,直到請(qǐng)求被響應(yīng)為止。通過(guò)這種方式去除對(duì)象之間的耦合。
  5. 狀態(tài)(State)模式:允許一個(gè)對(duì)象在其內(nèi)部狀態(tài)發(fā)生改變時(shí)改變其行為能力。
  6. 觀察者(Observer)模式:多個(gè)對(duì)象間存在一對(duì)多關(guān)系,當(dāng)一個(gè)對(duì)象發(fā)生改變時(shí),把這種改變通知給其他多個(gè)對(duì)象,從而影響其他對(duì)象的行為。
  7. 中介者(Mediator)模式:定義一個(gè)中介對(duì)象來(lái)簡(jiǎn)化原有對(duì)象之間的交互關(guān)系,降低系統(tǒng)中對(duì)象間的耦合度,使原有對(duì)象之間不必相互了解。
  8. 迭代器(Iterator)模式:提供一種方法來(lái)順序訪問(wèn)聚合對(duì)象中的一系列數(shù)據(jù),而不暴露聚合對(duì)象的內(nèi)部表示。
  9. 訪問(wèn)者(Visitor)模式:在不改變集合元素的前提下,為一個(gè)集合中的每個(gè)元素提供多種訪問(wèn)方式,即每個(gè)元素有多個(gè)訪問(wèn)者對(duì)象訪問(wèn)。
  10. 備忘錄(Memento)模式:在不破壞封裝性的前提下,獲取并保存一個(gè)對(duì)象的內(nèi)部狀態(tài),以便以后恢復(fù)它。
  11. 解釋器(Interpreter)模式:提供如何定義語(yǔ)言的文法,以及對(duì)語(yǔ)言句子的解釋方法,即解釋器。

以上 11 種行為型模式,除了模板方法模式和解釋器模式是類行為型模式,其他的全部屬于對(duì)象行為型模式,下面我們將詳細(xì)介紹它們的特點(diǎn)、結(jié)構(gòu)與應(yīng)用。

模板方法

在面向?qū)ο蟪绦蛟O(shè)計(jì)過(guò)程中,程序員常常會(huì)遇到這種情況:設(shè)計(jì)一個(gè)系統(tǒng)時(shí)知道了算法所需的關(guān)鍵步驟,而且確定了這些步驟的執(zhí)行順序,但某些步驟的具體實(shí)現(xiàn)還未知,或者說(shuō)某些步驟的實(shí)現(xiàn)與具體的環(huán)境相關(guān)。

例如,去銀行辦理業(yè)務(wù)一般要經(jīng)過(guò)以下4個(gè)流程:取號(hào)、排隊(duì)、辦理具體業(yè)務(wù)、對(duì)銀行工作人員進(jìn)行評(píng)分等,其中取號(hào)、排隊(duì)和對(duì)銀行工作人員進(jìn)行評(píng)分的業(yè)務(wù)對(duì)每個(gè)客戶是一樣的,可以在父類中實(shí)現(xiàn),但是辦理具體業(yè)務(wù)卻因人而異,它可能是存款、取款或者轉(zhuǎn)賬等,可以延遲到子類中實(shí)現(xiàn)。

這樣的例子在生活中還有很多,例如,一個(gè)人每天會(huì)起床、吃飯、做事、睡覺(jué)等,其中“做事”的內(nèi)容每天可能不同。我們把這些規(guī)定了流程或格式的實(shí)例定義成模板,允許使用者根據(jù)自己的需求去更新它,例如,簡(jiǎn)歷模板、論文模板、Word 中模板文件等。

以下介紹的模板方法模式將解決以上類似的問(wèn)題。

模式的定義與特點(diǎn)

模板方法(Template Method)模式的定義如下:定義一個(gè)操作中的算法骨架,而將算法的一些步驟延遲到子類中,使得子類可以不改變?cè)撍惴ńY(jié)構(gòu)的情況下重定義該算法的某些特定步驟。它是一種類行為型模式。

該模式的主要優(yōu)點(diǎn)如下。

  1. 它封裝了不變部分,擴(kuò)展可變部分。它把認(rèn)為是不變部分的算法封裝到父類中實(shí)現(xiàn),而把可變部分算法由子類繼承實(shí)現(xiàn),便于子類繼續(xù)擴(kuò)展。
  2. 它在父類中提取了公共的部分代碼,便于代碼復(fù)用。
  3. 部分方法是由子類實(shí)現(xiàn)的,因此子類可以通過(guò)擴(kuò)展方式增加相應(yīng)的功能,符合開(kāi)閉原則。

該模式的主要缺點(diǎn)如下。

  1. 對(duì)每個(gè)不同的實(shí)現(xiàn)都需要定義一個(gè)子類,這會(huì)導(dǎo)致類的個(gè)數(shù)增加,系統(tǒng)更加龐大,設(shè)計(jì)也更加抽象。
  2. 父類中的抽象方法由子類實(shí)現(xiàn),子類執(zhí)行的結(jié)果會(huì)影響父類的結(jié)果,這導(dǎo)致一種反向的控制結(jié)構(gòu),它提高了代碼閱讀的難度。

模式的結(jié)構(gòu)與實(shí)現(xiàn)

模板方法模式需要注意抽象類與具體子類之間的協(xié)作。它用到了虛函數(shù)的多態(tài)性技術(shù)以及“不用調(diào)用我,讓我來(lái)調(diào)用你”的反向控制技術(shù)。現(xiàn)在來(lái)介紹它們的基本結(jié)構(gòu)。

1. 模式的結(jié)構(gòu)

模板方法模式包含以下主要角色。

(1) 抽象類(Abstract Class):負(fù)責(zé)給出一個(gè)算法的輪廓和骨架。它由一個(gè)模板方法和若干個(gè)基本方法構(gòu)成。這些方法的定義如下。

① 模板方法:定義了算法的骨架,按某種順序調(diào)用其包含的基本方法。

② 基本方法:是整個(gè)算法中的一個(gè)步驟,包含以下幾種類型。

  • 抽象方法:在抽象類中申明,由具體子類實(shí)現(xiàn)。
  • 具體方法:在抽象類中已經(jīng)實(shí)現(xiàn),在具體子類中可以繼承或重寫(xiě)它。
  • 鉤子方法:在抽象類中已經(jīng)實(shí)現(xiàn),包括用于判斷的邏輯方法和需要子類重寫(xiě)的空方法兩種。

(2) 具體子類(Concrete Class):實(shí)現(xiàn)抽象類中所定義的抽象方法和鉤子方法,它們是一個(gè)頂級(jí)邏輯的一個(gè)組成步驟。

模板方法模式的結(jié)構(gòu)圖如圖 1 所示。

模板方法模式的結(jié)構(gòu)圖

策略模式

在現(xiàn)實(shí)生活中常常遇到實(shí)現(xiàn)某種目標(biāo)存在多種策略可供選擇的情況,例如,出行旅游可以乘坐飛機(jī)、乘坐火車、騎自行車或自己開(kāi)私家車等,超市促銷可以釆用打折、送商品、送積分等方法。

在軟件開(kāi)發(fā)中也常常遇到類似的情況,當(dāng)實(shí)現(xiàn)某一個(gè)功能存在多種算法或者策略,我們可以根據(jù)環(huán)境或者條件的不同選擇不同的算法或者策略來(lái)完成該功能,如數(shù)據(jù)排序策略有冒泡排序、選擇排序、插入排序、二叉樹(shù)排序等。

如果使用多重條件轉(zhuǎn)移語(yǔ)句實(shí)現(xiàn)(即硬編碼),不但使條件語(yǔ)句變得很復(fù)雜,而且增加、刪除或更換算法要修改原代碼,不易維護(hù),違背開(kāi)閉原則。如果采用策略模式就能很好解決該問(wèn)題。

策略模式的定義與特點(diǎn)

策略(Strategy)模式的定義:該模式定義了一系列算法,并將每個(gè)算法封裝起來(lái),使它們可以相互替換,且算法的變化不會(huì)影響使用算法的客戶。策略模式屬于對(duì)象行為模式,它通過(guò)對(duì)算法進(jìn)行封裝,把使用算法的責(zé)任和算法的實(shí)現(xiàn)分割開(kāi)來(lái),并委派給不同的對(duì)象對(duì)這些算法進(jìn)行管理。

策略模式的主要優(yōu)點(diǎn)如下。

  1. 多重條件語(yǔ)句不易維護(hù),而使用策略模式可以避免使用多重條件語(yǔ)句。
  2. 策略模式提供了一系列的可供重用的算法族,恰當(dāng)使用繼承可以把算法族的公共代碼轉(zhuǎn)移到父類里面,從而避免重復(fù)的代碼。
  3. 策略模式可以提供相同行為的不同實(shí)現(xiàn),客戶可以根據(jù)不同時(shí)間或空間要求選擇不同的。
  4. 策略模式提供了對(duì)開(kāi)閉原則的完美支持,可以在不修改原代碼的情況下,靈活增加新算法。
  5. 策略模式把算法的使用放到環(huán)境類中,而算法的實(shí)現(xiàn)移到具體策略類中,實(shí)現(xiàn)了二者的分離。

其主要缺點(diǎn)如下。

  1. 客戶端必須理解所有策略算法的區(qū)別,以便適時(shí)選擇恰當(dāng)?shù)乃惴悺?/li>
  2. 策略模式造成很多的策略類。

策略模式的結(jié)構(gòu)與實(shí)現(xiàn)

策略模式是準(zhǔn)備一組算法,并將這組算法封裝到一系列的策略類里面,作為一個(gè)抽象策略類的子類。策略模式的重心不是如何實(shí)現(xiàn)算法,而是如何組織這些算法,從而讓程序結(jié)構(gòu)更加靈活,具有更好的維護(hù)性和擴(kuò)展性,現(xiàn)在我們來(lái)分析其基本結(jié)構(gòu)和實(shí)現(xiàn)方法。

1. 模式的結(jié)構(gòu)

策略模式的主要角色如下。

  1. 抽象策略(Strategy)類:定義了一個(gè)公共接口,各種不同的算法以不同的方式實(shí)現(xiàn)這個(gè)接口,環(huán)境角色使用這個(gè)接口調(diào)用不同的算法,一般使用接口或抽象類實(shí)現(xiàn)。
  2. 具體策略(Concrete Strategy)類:實(shí)現(xiàn)了抽象策略定義的接口,提供具體的算法實(shí)現(xiàn)。
  3. 環(huán)境(Context)類:持有一個(gè)策略類的引用,最終給客戶端調(diào)用。

其結(jié)構(gòu)圖如圖 1 所示。

策略模式的結(jié)構(gòu)圖

責(zé)任鏈模式

在軟件開(kāi)發(fā)系統(tǒng)中,常常出現(xiàn)“方法的請(qǐng)求者”與“方法的實(shí)現(xiàn)者”之間存在緊密的耦合關(guān)系。這不利于軟件功能的擴(kuò)展與維護(hù)。例如,想對(duì)行為進(jìn)行“撤銷、重做、記錄”等處理都很不方便,因此“如何將方法的請(qǐng)求者與方法的實(shí)現(xiàn)者解耦?”變得很重要,命令模式能很好地解決這個(gè)問(wèn)題。

在現(xiàn)實(shí)生活中,這樣的例子也很多,例如,電視機(jī)遙控器(命令發(fā)送者)通過(guò)按鈕(具體命令)來(lái)遙控電視機(jī)(命令接收者),還有計(jì)算機(jī)鍵盤上的“功能鍵”等。

命令模式的定義與特點(diǎn)

命令(Command)模式的定義如下:將一個(gè)請(qǐng)求封裝為一個(gè)對(duì)象,使發(fā)出請(qǐng)求的責(zé)任和執(zhí)行請(qǐng)求的責(zé)任分割開(kāi)。這樣兩者之間通過(guò)命令對(duì)象進(jìn)行溝通,這樣方便將命令對(duì)象進(jìn)行儲(chǔ)存、傳遞、調(diào)用、增加與管理。

命令模式的主要優(yōu)點(diǎn)如下。

  1. 降低系統(tǒng)的耦合度。命令模式能將調(diào)用操作的對(duì)象與實(shí)現(xiàn)該操作的對(duì)象解耦。
  2. 增加或刪除命令非常方便。采用命令模式增加與刪除命令不會(huì)影響其他類,它滿足“開(kāi)閉原則”,對(duì)擴(kuò)展比較靈活。
  3. 可以實(shí)現(xiàn)宏命令。命令模式可以與組合模式結(jié)合,將多個(gè)命令裝配成一個(gè)組合命令,即宏命令。
  4. 方便實(shí)現(xiàn) Undo 和 Redo 操作。命令模式可以與后面介紹的備忘錄模式結(jié)合,實(shí)現(xiàn)命令的撤銷與恢復(fù)。

其缺點(diǎn)是:可能產(chǎn)生大量具體命令類。因?yàn)橛?jì)對(duì)每一個(gè)具體操作都需要設(shè)計(jì)一個(gè)具體命令類,這將增加系統(tǒng)的復(fù)雜性。

命令模式的結(jié)構(gòu)與實(shí)現(xiàn)

可以將系統(tǒng)中的相關(guān)操作抽象成命令,使調(diào)用者與實(shí)現(xiàn)者相關(guān)分離,其結(jié)構(gòu)如下。

1. 模式的結(jié)構(gòu)

命令模式包含以下主要角色。

  1. 抽象命令類(Command)角色:聲明執(zhí)行命令的接口,擁有執(zhí)行命令的抽象方法 execute()。
  2. 具體命令角色(Concrete Command)角色:是抽象命令類的具體實(shí)現(xiàn)類,它擁有接收者對(duì)象,并通過(guò)調(diào)用接收者的功能來(lái)完成命令要執(zhí)行的操作。
  3. 實(shí)現(xiàn)者/接收者(Receiver)角色:執(zhí)行命令功能的相關(guān)操作,是具體命令對(duì)象業(yè)務(wù)的真正實(shí)現(xiàn)者。
  4. 調(diào)用者/請(qǐng)求者(Invoker)角色:是請(qǐng)求的發(fā)送者,它通常擁有很多的命令對(duì)象,并通過(guò)訪問(wèn)命令對(duì)象來(lái)執(zhí)行相關(guān)請(qǐng)求,它不直接訪問(wèn)接收者。

其結(jié)構(gòu)圖如圖 1 所示。

命令模式的結(jié)構(gòu)圖

責(zé)任鏈模式

在現(xiàn)實(shí)生活中,常常會(huì)出現(xiàn)這樣的事例:一個(gè)請(qǐng)求有多個(gè)對(duì)象可以處理,但每個(gè)對(duì)象的處理?xiàng)l件或權(quán)限不同。例如,公司員工請(qǐng)假,可批假的領(lǐng)導(dǎo)有部門負(fù)責(zé)人、副總經(jīng)理、總經(jīng)理等,但每個(gè)領(lǐng)導(dǎo)能批準(zhǔn)的天數(shù)不同,員工必須根據(jù)自己要請(qǐng)假的天數(shù)去找不同的領(lǐng)導(dǎo)簽名,也就是說(shuō)員工必須記住每個(gè)領(lǐng)導(dǎo)的姓名、電話和地址等信息,這增加了難度。這樣的例子還有很多,如找領(lǐng)導(dǎo)出差報(bào)銷、生活中的“擊鼓傳花”游戲等。

在計(jì)算機(jī)軟硬件中也有相關(guān)例子,如總線網(wǎng)中數(shù)據(jù)報(bào)傳送,每臺(tái)計(jì)算機(jī)根據(jù)目標(biāo)地址是否同自己的地址相同來(lái)決定是否接收;還有異常處理中,處理程序根據(jù)異常的類型決定自己是否處理該異常;還有 Struts2 的攔截器、JSPServlet 的 Filter 等,所有這些,如果用責(zé)任鏈模式都能很好解決。

模式的定義與特點(diǎn)

責(zé)任鏈(Chain of Responsibility)模式的定義:為了避免請(qǐng)求發(fā)送者與多個(gè)請(qǐng)求處理者耦合在一起,將所有請(qǐng)求的處理者通過(guò)前一對(duì)象記住其下一個(gè)對(duì)象的引用而連成一條鏈;當(dāng)有請(qǐng)求發(fā)生時(shí),可將請(qǐng)求沿著這條鏈傳遞,直到有對(duì)象處理它為止。

注意:責(zé)任鏈模式也叫職責(zé)鏈模式。

在責(zé)任鏈模式中,客戶只需要將請(qǐng)求發(fā)送到責(zé)任鏈上即可,無(wú)須關(guān)心請(qǐng)求的處理細(xì)節(jié)和請(qǐng)求的傳遞過(guò)程,所以責(zé)任鏈將請(qǐng)求的發(fā)送者和請(qǐng)求的處理者解耦了。

責(zé)任鏈模式是一種對(duì)象行為型模式,其主要優(yōu)點(diǎn)如下。

  1. 降低了對(duì)象之間的耦合度。該模式使得一個(gè)對(duì)象無(wú)須知道到底是哪一個(gè)對(duì)象處理其請(qǐng)求以及鏈的結(jié)構(gòu),發(fā)送者和接收者也無(wú)須擁有對(duì)方的明確信息。
  2. 增強(qiáng)了系統(tǒng)的可擴(kuò)展性??梢愿鶕?jù)需要增加新的請(qǐng)求處理類,滿足開(kāi)閉原則。
  3. 增強(qiáng)了給對(duì)象指派職責(zé)的靈活性。當(dāng)工作流程發(fā)生變化,可以動(dòng)態(tài)地改變鏈內(nèi)的成員或者調(diào)動(dòng)它們的次序,也可動(dòng)態(tài)地新增或者刪除責(zé)任。
  4. 責(zé)任鏈簡(jiǎn)化了對(duì)象之間的連接。每個(gè)對(duì)象只需保持一個(gè)指向其后繼者的引用,不需保持其他所有處理者的引用,這避免了使用眾多的 if 或者 if···else 語(yǔ)句。
  5. 責(zé)任分擔(dān)。每個(gè)類只需要處理自己該處理的工作,不該處理的傳遞給下一個(gè)對(duì)象完成,明確各類的責(zé)任范圍,符合類的單一職責(zé)原則。

其主要缺點(diǎn)如下。

  1. 不能保證每個(gè)請(qǐng)求一定被處理。由于一個(gè)請(qǐng)求沒(méi)有明確的接收者,所以不能保證它一定會(huì)被處理,該請(qǐng)求可能一直傳到鏈的末端都得不到處理。
  2. 對(duì)比較長(zhǎng)的職責(zé)鏈,請(qǐng)求的處理可能涉及多個(gè)處理對(duì)象,系統(tǒng)性能將受到一定影響。
  3. 職責(zé)鏈建立的合理性要靠客戶端來(lái)保證,增加了客戶端的復(fù)雜性,可能會(huì)由于職責(zé)鏈的錯(cuò)誤設(shè)置而導(dǎo)致系統(tǒng)出錯(cuò),如可能會(huì)造成循環(huán)調(diào)用。

模式的結(jié)構(gòu)與實(shí)現(xiàn)

通常情況下,可以通過(guò)數(shù)據(jù)鏈表來(lái)實(shí)現(xiàn)職責(zé)鏈模式的數(shù)據(jù)結(jié)構(gòu)。

1. 模式的結(jié)構(gòu)

職責(zé)鏈模式主要包含以下角色。

  1. 抽象處理者(Handler)角色:定義一個(gè)處理請(qǐng)求的接口,包含抽象處理方法和一個(gè)后繼連接。
  2. 具體處理者(Concrete Handler)角色:實(shí)現(xiàn)抽象處理者的處理方法,判斷能否處理本次請(qǐng)求,如果可以處理請(qǐng)求則處理,否則將該請(qǐng)求轉(zhuǎn)給它的后繼者。
  3. 客戶類(Client)角色:創(chuàng)建處理鏈,并向鏈頭的具體處理者對(duì)象提交請(qǐng)求,它不關(guān)心處理細(xì)節(jié)和請(qǐng)求的傳遞過(guò)程。

其結(jié)構(gòu)圖如圖 1 所示??蛻舳丝砂磮D 2 所示設(shè)置責(zé)任鏈。

責(zé)任鏈模式的結(jié)構(gòu)圖

圖1 責(zé)任鏈模式的結(jié)構(gòu)圖

責(zé)任鏈

責(zé)任了模式代碼

package chainOfResponsibility;
public class ChainOfResponsibilityPattern
{
    public static void main(String[] args)
    {
        //組裝責(zé)任鏈 
        Handler handler1=new ConcreteHandler1(); 
        Handler handler2=new ConcreteHandler2(); 
        handler1.setNext(handler2); 
        //提交請(qǐng)求 
        handler1.handleRequest("two");
    }
}
//抽象處理者角色
abstract class Handler
{
    private Handler next;
    public void setNext(Handler next)
    {
        this.next=next; 
    }
    public Handler getNext()
    { 
        return next; 
    }   
    //處理請(qǐng)求的方法
    public abstract void handleRequest(String request);       
}
//具體處理者角色1
class ConcreteHandler1 extends Handler
{
    public void handleRequest(String request)
    {
        if(request.equals("one")) 
        {
            System.out.println("具體處理者1負(fù)責(zé)處理該請(qǐng)求!");       
        }
        else
        {
            if(getNext()!=null) 
            {
                getNext().handleRequest(request);             
            }
            else
            {
                System.out.println("沒(méi)有人處理該請(qǐng)求!");
            }
        } 
    } 
}
//具體處理者角色2
class ConcreteHandler2 extends Handler
{
    public void handleRequest(String request)
    {
        if(request.equals("two")) 
        {
            System.out.println("具體處理者2負(fù)責(zé)處理該請(qǐng)求!");       
        }
        else
        {
            if(getNext()!=null) 
            {
                getNext().handleRequest(request);             
            }
            else
            {
                System.out.println("沒(méi)有人處理該請(qǐng)求!");
            }
        } 
    }
}

狀態(tài)模式:

在軟件開(kāi)發(fā)過(guò)程中,應(yīng)用程序中的有些對(duì)象可能會(huì)根據(jù)不同的情況做出不同的行為,我們把這種對(duì)象稱為有狀態(tài)的對(duì)象,而把影響對(duì)象行為的一個(gè)或多個(gè)動(dòng)態(tài)變化的屬性稱為狀態(tài)。當(dāng)有狀態(tài)的對(duì)象與外部事件產(chǎn)生互動(dòng)時(shí),其內(nèi)部狀態(tài)會(huì)發(fā)生改變,從而使得其行為也隨之發(fā)生改變。如人的情緒有高興的時(shí)候和傷心的時(shí)候,不同的情緒有不同的行為,當(dāng)然外界也會(huì)影響其情緒變化。

對(duì)這種有狀態(tài)的對(duì)象編程,傳統(tǒng)的解決方案是:將這些所有可能發(fā)生的情況全都考慮到,然后使用 if-else 語(yǔ)句來(lái)做狀態(tài)判斷,再進(jìn)行不同情況的處理。但當(dāng)對(duì)象的狀態(tài)很多時(shí),程序會(huì)變得很復(fù)雜。而且增加新的狀態(tài)要添加新的 if-else 語(yǔ)句,這違背了“開(kāi)閉原則”,不利于程序的擴(kuò)展。

以上問(wèn)題如果采用“狀態(tài)模式”就能很好地得到解決。狀態(tài)模式的解決思想是:當(dāng)控制一個(gè)對(duì)象狀態(tài)轉(zhuǎn)換的條件表達(dá)式過(guò)于復(fù)雜時(shí),把相關(guān)“判斷邏輯”提取出來(lái),放到一系列的狀態(tài)類當(dāng)中,這樣可以把原來(lái)復(fù)雜的邏輯判斷簡(jiǎn)單化。

狀態(tài)模式的定義與特點(diǎn)

狀態(tài)(State)模式的定義:對(duì)有狀態(tài)的對(duì)象,把復(fù)雜的“判斷邏輯”提取到不同的狀態(tài)對(duì)象中,允許狀態(tài)對(duì)象在其內(nèi)部狀態(tài)發(fā)生改變時(shí)改變其行為。

狀態(tài)模式是一種對(duì)象行為型模式,其主要優(yōu)點(diǎn)如下。

  1. 狀態(tài)模式將與特定狀態(tài)相關(guān)的行為局部化到一個(gè)狀態(tài)中,并且將不同狀態(tài)的行為分割開(kāi)來(lái),滿足“單一職責(zé)原則”。
  2. 減少對(duì)象間的相互依賴。將不同的狀態(tài)引入獨(dú)立的對(duì)象中會(huì)使得狀態(tài)轉(zhuǎn)換變得更加明確,且減少對(duì)象間的相互依賴。
  3. 有利于程序的擴(kuò)展。通過(guò)定義新的子類很容易地增加新的狀態(tài)和轉(zhuǎn)換。

狀態(tài)模式的主要缺點(diǎn)如下。

  1. 狀態(tài)模式的使用必然會(huì)增加系統(tǒng)的類與對(duì)象的個(gè)數(shù)。
  2. 狀態(tài)模式的結(jié)構(gòu)與實(shí)現(xiàn)都較為復(fù)雜,如果使用不當(dāng)會(huì)導(dǎo)致程序結(jié)構(gòu)和代碼的混亂。

狀態(tài)模式的結(jié)構(gòu)與實(shí)現(xiàn)

狀態(tài)模式把受環(huán)境改變的對(duì)象行為包裝在不同的狀態(tài)對(duì)象里,其意圖是讓一個(gè)對(duì)象在其內(nèi)部狀態(tài)改變的時(shí)候,其行為也隨之改變?,F(xiàn)在我們來(lái)分析其基本結(jié)構(gòu)和實(shí)現(xiàn)方法。

1. 模式的結(jié)構(gòu)

狀態(tài)模式包含以下主要角色。

  1. 環(huán)境(Context)角色:也稱為上下文,它定義了客戶感興趣的接口,維護(hù)一個(gè)當(dāng)前狀態(tài),并將與狀態(tài)相關(guān)的操作委托給當(dāng)前狀態(tài)對(duì)象來(lái)處理。
  2. 抽象狀態(tài)(State)角色:定義一個(gè)接口,用以封裝環(huán)境對(duì)象中的特定狀態(tài)所對(duì)應(yīng)的行為。
  3. 具體狀態(tài)(Concrete State)角色:實(shí)現(xiàn)抽象狀態(tài)所對(duì)應(yīng)的行為。

其結(jié)構(gòu)圖如圖 1 所示。

狀態(tài)模式的結(jié)構(gòu)圖
package state;
public class StatePatternClient
{
    public static void main(String[] args)
    {       
        Context context=new Context();    //創(chuàng)建環(huán)境       
        context.Handle();    //處理請(qǐng)求
        context.Handle();
        context.Handle();
        context.Handle();
    }
}
//環(huán)境類
class Context
{
    private State state;
    //定義環(huán)境類的初始狀態(tài)
    public Context()
    {
        this.state=new ConcreteStateA();
    }
    //設(shè)置新?tīng)顟B(tài)
    public void setState(State state)
    {
        this.state=state;
    }
    //讀取狀態(tài)
    public State getState()
    {
        return(state);
    }
    //對(duì)請(qǐng)求做處理
    public void Handle()
    {
        state.Handle(this);
    }
}
//抽象狀態(tài)類
abstract class State
{
    public abstract void Handle(Context context);
}
//具體狀態(tài)A類
class ConcreteStateA extends State
{
    public void Handle(Context context)
    {
        System.out.println("當(dāng)前狀態(tài)是 A.");
        context.setState(new ConcreteStateB());
    }
}
//具體狀態(tài)B類
class ConcreteStateB extends State
{
    public void Handle(Context context)
    {
        System.out.println("當(dāng)前狀態(tài)是 B.");
        context.setState(new ConcreteStateA());
    }
}

觀察者模式

在現(xiàn)實(shí)世界中,許多對(duì)象并不是獨(dú)立存在的,其中一個(gè)對(duì)象的行為發(fā)生改變可能會(huì)導(dǎo)致一個(gè)或者多個(gè)其他對(duì)象的行為也發(fā)生改變。例如,某種商品的物價(jià)上漲時(shí)會(huì)導(dǎo)致部分商家高興,而消費(fèi)者傷心;還有,當(dāng)我們開(kāi)車到交叉路口時(shí),遇到紅燈會(huì)停,遇到綠燈會(huì)行。這樣的例子還有很多,例如,股票價(jià)格與股民、微信公眾號(hào)與微信用戶、氣象局的天氣預(yù)報(bào)與聽(tīng)眾、小偷與警察等。

在軟件世界也是這樣,例如,Excel 中的數(shù)據(jù)與折線圖、餅狀圖、柱狀圖之間的關(guān)系;MVC 模式中的模型與視圖的關(guān)系;事件模型中的事件源與事件處理者。所有這些,如果用觀察者模式來(lái)實(shí)現(xiàn)就非常方便。

模式的定義與特點(diǎn)

觀察者(Observer)模式的定義:指多個(gè)對(duì)象間存在一對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都得到通知并被自動(dòng)更新。這種模式有時(shí)又稱作發(fā)布-訂閱模式、模型-視圖模式,它是對(duì)象行為型模式。

觀察者模式是一種對(duì)象行為型模式,其主要優(yōu)點(diǎn)如下。

  1. 降低了目標(biāo)與觀察者之間的耦合關(guān)系,兩者之間是抽象耦合關(guān)系。
  2. 目標(biāo)與觀察者之間建立了一套觸發(fā)機(jī)制。

它的主要缺點(diǎn)如下。

  1. 目標(biāo)與觀察者之間的依賴關(guān)系并沒(méi)有完全解除,而且有可能出現(xiàn)循環(huán)引用。
  2. 當(dāng)觀察者對(duì)象很多時(shí),通知的發(fā)布會(huì)花費(fèi)很多時(shí)間,影響程序的效率。

模式的結(jié)構(gòu)與實(shí)現(xiàn)

實(shí)現(xiàn)觀察者模式時(shí)要注意具體目標(biāo)對(duì)象和具體觀察者對(duì)象之間不能直接調(diào)用,否則將使兩者之間緊密耦合起來(lái),這違反了面向?qū)ο蟮脑O(shè)計(jì)原則。

1. 模式的結(jié)構(gòu)

觀察者模式的主要角色如下。

  1. 抽象主題(Subject)角色:也叫抽象目標(biāo)類,它提供了一個(gè)用于保存觀察者對(duì)象的聚集類和增加、刪除觀察者對(duì)象的方法,以及通知所有觀察者的抽象方法。
  2. 具體主題(Concrete Subject)角色:也叫具體目標(biāo)類,它實(shí)現(xiàn)抽象目標(biāo)中的通知方法,當(dāng)具體主題的內(nèi)部狀態(tài)發(fā)生改變時(shí),通知所有注冊(cè)過(guò)的觀察者對(duì)象。
  3. 抽象觀察者(Observer)角色:它是一個(gè)抽象類或接口,它包含了一個(gè)更新自己的抽象方法,當(dāng)接到具體主題的更改通知時(shí)被調(diào)用。
  4. 具體觀察者(Concrete Observer)角色:實(shí)現(xiàn)抽象觀察者中定義的抽象方法,以便在得到目標(biāo)的更改通知時(shí)更新自身的狀態(tài)。

觀察者模式的結(jié)構(gòu)圖如圖 1 所示。

觀察者模式的結(jié)構(gòu)圖
ackage observer;
import java.util.*;
public class ObserverPattern
{
    public static void main(String[] args)
    {
        Subject subject=new ConcreteSubject();
        Observer obs1=new ConcreteObserver1();
        Observer obs2=new ConcreteObserver2();
        subject.add(obs1);
        subject.add(obs2);
        subject.notifyObserver();
    }
}
//抽象目標(biāo)
abstract class Subject
{
    protected List<Observer> observers=new ArrayList<Observer>();   
    //增加觀察者方法
    public void add(Observer observer)
    {
        observers.add(observer);
    }    
    //刪除觀察者方法
    public void remove(Observer observer)
    {
        observers.remove(observer);
    }   
    public abstract void notifyObserver(); //通知觀察者方法
}
//具體目標(biāo)
class ConcreteSubject extends Subject
{
    public void notifyObserver()
    {
        System.out.println("具體目標(biāo)發(fā)生改變...");
        System.out.println("--------------");       
       
        for(Object obs:observers)
        {
            ((Observer)obs).response();
        }
       
    }          
}
//抽象觀察者
interface Observer
{
    void response(); //反應(yīng)
}
//具體觀察者1
class ConcreteObserver1 implements Observer
{
    public void response()
    {
        System.out.println("具體觀察者1作出反應(yīng)!");
    }
}
//具體觀察者1
class ConcreteObserver2 implements Observer
{
    public void response()
    {
        System.out.println("具體觀察者2作出反應(yīng)!");
    }
}

在jdk中也內(nèi)置了Observable(抽象被觀察者),Observer(抽象觀察者)這兩個(gè)類,我們也可以直接拿來(lái)用

public interface Observer {//(抽象觀察者
    //只定義了一個(gè)update方法
    void update(Observable o, Object arg);
}

public class Observable {//抽象被觀察者
    private boolean changed = false;//定義改變狀態(tài),默認(rèn)為false
    private final ArrayList<Observer> observers;//定義一個(gè)觀察者list

    public Observable() {//構(gòu)造函數(shù),初始化一個(gè)觀察者list來(lái)保存觀察者
        observers = new ArrayList<>();
    }
    //添加觀察者,帶同步字段的,所以是線程安全的
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!observers.contains(o)) {
            observers.add(o);
        }
    }

    //刪除觀察者
    public synchronized void deleteObserver(Observer o) {
        observers.remove(o);
    }

    //通知所以觀察者,無(wú)參數(shù)
    public void notifyObservers() {
        notifyObservers(null);
    }

     //通知所有觀察者,帶參數(shù)
    public void notifyObservers(Object arg) {

        Observer[] arrLocal;
        //加synchronized字段,保證多線程下操作沒(méi)有問(wèn)題
        synchronized (this) {
            if (!hasChanged())//這里做了是否發(fā)生改變的判斷,是為了防止出現(xiàn)無(wú)意義的更新
                return;

            arrLocal = observers.toArray(new Observer[observers.size()]);//ArrayList轉(zhuǎn)換成一個(gè)臨時(shí)的數(shù)組,這樣就防止了通知,添加,移除同時(shí)發(fā)生可能導(dǎo)致的異常
            clearChanged();///清除改變狀態(tài),設(shè)置為false
        }
        //遍歷逐一通知
        for (int i = arrLocal.length-1; i>=0; i--)
            arrLocal[i].update(this, arg);
    }

    //清楚所有觀察者
    public synchronized void deleteObservers() {
        observers.clear();
    }

    //設(shè)置被觀察者為改變狀態(tài),設(shè)置為true
    protected synchronized void setChanged() {
        changed = true;
    }

    //清除改變狀態(tài),設(shè)置為false
    protected synchronized void clearChanged() {
        changed = false;
    }

    //返回當(dāng)前的改變狀態(tài)
    public synchronized boolean hasChanged() {
        return changed;
    }

    //觀察者數(shù)量
    public synchronized int countObservers() {
        return observers.size();
    }
}

中介者模式

在現(xiàn)實(shí)生活中,常常會(huì)出現(xiàn)好多對(duì)象之間存在復(fù)雜的交互關(guān)系,這種交互關(guān)系常常是“網(wǎng)狀結(jié)構(gòu)”,它要求每個(gè)對(duì)象都必須知道它需要交互的對(duì)象。例如,每個(gè)人必須記住他(她)所有朋友的電話;而且,朋友中如果有人的電話修改了,他(她)必須告訴其他所有的朋友修改,這叫作“牽一發(fā)而動(dòng)全身”,非常復(fù)雜。

如果把這種“網(wǎng)狀結(jié)構(gòu)”改為“星形結(jié)構(gòu)”的話,將大大降低它們之間的“耦合性”,這時(shí)只要找一個(gè)“中介者”就可以了。如前面所說(shuō)的“每個(gè)人必須記住所有朋友電話”的問(wèn)題,只要在網(wǎng)上建立一個(gè)每個(gè)朋友都可以訪問(wèn)的“通信錄”就解決了。這樣的例子還有很多,例如,你剛剛參力口工作想租房,可以找“房屋中介”;或者,自己剛剛到一個(gè)陌生城市找工作,可以找“人才交流中心”幫忙。

在軟件的開(kāi)發(fā)過(guò)程中,這樣的例子也很多,例如,在 MVC 框架中,控制器(C)就是模型(M)和視圖(V)的中介者;還有大家常用的 QQ 聊天程序的“中介者”是 QQ 服務(wù)器。所有這些,都可以采用“中介者模式”來(lái)實(shí)現(xiàn),它將大大降低對(duì)象之間的耦合性,提高系統(tǒng)的靈活性。

模式的定義與特點(diǎn)

中介者(Mediator)模式的定義:定義一個(gè)中介對(duì)象來(lái)封裝一系列對(duì)象之間的交互,使原有對(duì)象之間的耦合松散,且可以獨(dú)立地改變它們之間的交互。中介者模式又叫調(diào)停模式,它是迪米特法則的典型應(yīng)用。

中介者模式是一種對(duì)象行為型模式,其主要優(yōu)點(diǎn)如下。

  1. 降低了對(duì)象之間的耦合性,使得對(duì)象易于獨(dú)立地被復(fù)用。
  2. 將對(duì)象間的一對(duì)多關(guān)聯(lián)轉(zhuǎn)變?yōu)橐粚?duì)一的關(guān)聯(lián),提高系統(tǒng)的靈活性,使得系統(tǒng)易于維護(hù)和擴(kuò)展。

其主要缺點(diǎn)是:當(dāng)同事類太多時(shí),中介者的職責(zé)將很大,它會(huì)變得復(fù)雜而龐大,以至于系統(tǒng)難以維護(hù)。

模式的結(jié)構(gòu)與實(shí)現(xiàn)

中介者模式實(shí)現(xiàn)的關(guān)鍵是找出“中介者”,下面對(duì)它的結(jié)構(gòu)和實(shí)現(xiàn)進(jìn)行分析。

1. 模式的結(jié)構(gòu)

中介者模式包含以下主要角色。

  1. 抽象中介者(Mediator)角色:它是中介者的接口,提供了同事對(duì)象注冊(cè)與轉(zhuǎn)發(fā)同事對(duì)象信息的抽象方法。
  2. 具體中介者(ConcreteMediator)角色:實(shí)現(xiàn)中介者接口,定義一個(gè) List 來(lái)管理同事對(duì)象,協(xié)調(diào)各個(gè)同事角色之間的交互關(guān)系,因此它依賴于同事角色。
  3. 抽象同事類(Colleague)角色:定義同事類的接口,保存中介者對(duì)象,提供同事對(duì)象交互的抽象方法,實(shí)現(xiàn)所有相互影響的同事類的公共功能。
  4. 具體同事類(Concrete Colleague)角色:是抽象同事類的實(shí)現(xiàn)者,當(dāng)需要與其他同事對(duì)象交互時(shí),由中介者對(duì)象負(fù)責(zé)后續(xù)的交互。

中介者模式的結(jié)構(gòu)圖如圖 1 所示。

中介者模式的結(jié)構(gòu)圖
package mediator;
import java.util.*;
public class MediatorPattern
{
    public static void main(String[] args)
    {
        Mediator md=new ConcreteMediator();
        Colleague c1,c2;
        c1=new ConcreteColleague1();
        c2=new ConcreteColleague2();
        md.register(c1);
        md.register(c2);
        c1.send();
        System.out.println("-------------");
        c2.send();
    }
}
//抽象中介者
abstract class Mediator
{
    public abstract void register(Colleague colleague);
    public abstract void relay(Colleague cl); //轉(zhuǎn)發(fā)
}
//具體中介者
class ConcreteMediator extends Mediator
{
    private List<Colleague> colleagues=new ArrayList<Colleague>();
    public void register(Colleague colleague)
    {
        if(!colleagues.contains(colleague))
        {
            colleagues.add(colleague);
            colleague.setMedium(this);
        }
    }
    public void relay(Colleague cl)
    {
        for(Colleague ob:colleagues)
        {
            if(!ob.equals(cl))
            {
                ((Colleague)ob).receive();
            }   
        }
    }
}
//抽象同事類
abstract class Colleague
{
    protected Mediator mediator;
    public void setMedium(Mediator mediator)
    {
        this.mediator=mediator;
    }   
    public abstract void receive();   
    public abstract void send();
}
//具體同事類
class ConcreteColleague1 extends Colleague
{
    public void receive()
    {
        System.out.println("具體同事類1收到請(qǐng)求。");
    }   
    public void send()
    {
        System.out.println("具體同事類1發(fā)出請(qǐng)求。");
        mediator.relay(this); //請(qǐng)中介者轉(zhuǎn)發(fā)
    }
}
//具體同事類
class ConcreteColleague2 extends Colleague
{
    public void receive()
    {
        System.out.println("具體同事類2收到請(qǐng)求。");
    }   
    public void send()
    {
        System.out.println("具體同事類2發(fā)出請(qǐng)求。");
        mediator.relay(this); //請(qǐng)中介者轉(zhuǎn)發(fā)
    }
}

迭代器模式

在現(xiàn)實(shí)生活以及程序設(shè)計(jì)中,經(jīng)常要訪問(wèn)一個(gè)聚合對(duì)象中的各個(gè)元素,如“數(shù)據(jù)結(jié)構(gòu)”中的鏈表遍歷,通常的做法是將鏈表的創(chuàng)建和遍歷都放在同一個(gè)類中,但這種方式不利于程序的擴(kuò)展,如果要更換遍歷方法就必須修改程序源代碼,這違背了 “開(kāi)閉原則”。

既然將遍歷方法封裝在聚合類中不可取,那么聚合類中不提供遍歷方法,將遍歷方法由用戶自己實(shí)現(xiàn)是否可行呢?答案是同樣不可取,因?yàn)檫@種方式會(huì)存在兩個(gè)缺點(diǎn):

  1. 暴露了聚合類的內(nèi)部表示,使其數(shù)據(jù)不安全;
  2. 增加了客戶的負(fù)擔(dān)。

“迭代器模式”能較好地克服以上缺點(diǎn),它在客戶訪問(wèn)類與聚合類之間插入一個(gè)迭代器,這分離了聚合對(duì)象與其遍歷行為,對(duì)客戶也隱藏了其內(nèi)部細(xì)節(jié),且滿足“單一職責(zé)原則”和“開(kāi)閉原則”,如 Java 中的 Collection、List、Set、Map 等都包含了迭代器。

模式的定義與特點(diǎn)

迭代器(Iterator)模式的定義:提供一個(gè)對(duì)象來(lái)順序訪問(wèn)聚合對(duì)象中的一系列數(shù)據(jù),而不暴露聚合對(duì)象的內(nèi)部表示。迭代器模式是一種對(duì)象行為型模式,其主要優(yōu)點(diǎn)如下。

  1. 訪問(wèn)一個(gè)聚合對(duì)象的內(nèi)容而無(wú)須暴露它的內(nèi)部表示。
  2. 遍歷任務(wù)交由迭代器完成,這簡(jiǎn)化了聚合類。
  3. 它支持以不同方式遍歷一個(gè)聚合,甚至可以自定義迭代器的子類以支持新的遍歷。
  4. 增加新的聚合類和迭代器類都很方便,無(wú)須修改原有代碼。
  5. 封裝性良好,為遍歷不同的聚合結(jié)構(gòu)提供一個(gè)統(tǒng)一的接口。

其主要缺點(diǎn)是:增加了類的個(gè)數(shù),這在一定程度上增加了系統(tǒng)的復(fù)雜性。

模式的結(jié)構(gòu)與實(shí)現(xiàn)

迭代器模式是通過(guò)將聚合對(duì)象的遍歷行為分離出來(lái),抽象成迭代器類來(lái)實(shí)現(xiàn)的,其目的是在不暴露聚合對(duì)象的內(nèi)部結(jié)構(gòu)的情況下,讓外部代碼透明地訪問(wèn)聚合的內(nèi)部數(shù)據(jù)?,F(xiàn)在我們來(lái)分析其基本結(jié)構(gòu)與實(shí)現(xiàn)方法。

1. 模式的結(jié)構(gòu)

迭代器模式主要包含以下角色。

  1. 抽象聚合(Aggregate)角色:定義存儲(chǔ)、添加、刪除聚合對(duì)象以及創(chuàng)建迭代器對(duì)象的接口。
  2. 具體聚合(ConcreteAggregate)角色:實(shí)現(xiàn)抽象聚合類,返回一個(gè)具體迭代器的實(shí)例。
  3. 抽象迭代器(Iterator)角色:定義訪問(wèn)和遍歷聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。
  4. 具體迭代器(Concretelterator)角色:實(shí)現(xiàn)抽象迭代器接口中所定義的方法,完成對(duì)聚合對(duì)象的遍歷,記錄遍歷的當(dāng)前位置。

其結(jié)構(gòu)圖如圖 1 所示。

迭代器模式的結(jié)構(gòu)圖
package iterator;
import java.util.*;
public class IteratorPattern
{
    public static void main(String[] args)
    {
        Aggregate ag=new ConcreteAggregate(); 
        ag.add("中山大學(xué)"); 
        ag.add("華南理工"); 
        ag.add("韶關(guān)學(xué)院");
        System.out.print("聚合的內(nèi)容有:");
        Iterator it=ag.getIterator(); 
        while(it.hasNext())
        { 
            Object ob=it.next(); 
            System.out.print(ob.toString()+"\t"); 
        }
        Object ob=it.first();
        System.out.println("\nFirst:"+ob.toString());
    }
}
//抽象聚合
interface Aggregate
{ 
    public void add(Object obj); 
    public void remove(Object obj); 
    public Iterator getIterator(); 
}
//具體聚合
class ConcreteAggregate implements Aggregate
{ 
    private List<Object> list=new ArrayList<Object>(); 
    public void add(Object obj)
    { 
        list.add(obj); 
    }
    public void remove(Object obj)
    { 
        list.remove(obj); 
    }
    public Iterator getIterator()
    { 
        return(new ConcreteIterator(list)); 
    }     
}
//抽象迭代器
interface Iterator
{
    Object first();
    Object next();
    boolean hasNext();
}
//具體迭代器
class ConcreteIterator implements Iterator
{ 
    private List<Object> list=null; 
    private int index=-1; 
    public ConcreteIterator(List<Object> list)
    { 
        this.list=list; 
    } 
    public boolean hasNext()
    { 
        if(index<list.size()-1)
        { 
            return true;
        }
        else
        {
            return false;
        }
    }
    public Object first()
    {
        index=0;
        Object obj=list.get(index);;
        return obj;
    }
    public Object next()
    { 
        Object obj=null; 
        if(this.hasNext())
        { 
            obj=list.get(++index); 
        } 
        return obj; 
    }   
}

訪問(wèn)者模式

在現(xiàn)實(shí)生活中,有些集合對(duì)象中存在多種不同的元素,且每種元素也存在多種不同的訪問(wèn)者和處理方式。例如,公園中存在多個(gè)景點(diǎn),也存在多個(gè)游客,不同的游客對(duì)同一個(gè)景點(diǎn)的評(píng)價(jià)可能不同;醫(yī)院醫(yī)生開(kāi)的處方單中包含多種藥元素,査看它的劃價(jià)員和藥房工作人員對(duì)它的處理方式也不同,劃價(jià)員根據(jù)處方單上面的藥品名和數(shù)量進(jìn)行劃價(jià),藥房工作人員根據(jù)處方單的內(nèi)容進(jìn)行抓藥。

這樣的例子還有很多,例如,電影或電視劇中的人物角色,不同的觀眾對(duì)他們的評(píng)價(jià)也不同;還有顧客在商場(chǎng)購(gòu)物時(shí)放在“購(gòu)物車”中的商品,顧客主要關(guān)心所選商品的性價(jià)比,而收銀員關(guān)心的是商品的價(jià)格和數(shù)量。

這些被處理的數(shù)據(jù)元素相對(duì)穩(wěn)定而訪問(wèn)方式多種多樣的數(shù)據(jù)結(jié)構(gòu),如果用“訪問(wèn)者模式”來(lái)處理比較方便。訪問(wèn)者模式能把處理方法從數(shù)據(jù)結(jié)構(gòu)中分離出來(lái),并可以根據(jù)需要增加新的處理方法,且不用修改原來(lái)的程序代碼與數(shù)據(jù)結(jié)構(gòu),這提高了程序的擴(kuò)展性和靈活性。

模式的定義與特點(diǎn)

訪問(wèn)者(Visitor)模式的定義:將作用于某種數(shù)據(jù)結(jié)構(gòu)中的各元素的操作分離出來(lái)封裝成獨(dú)立的類,使其在不改變數(shù)據(jù)結(jié)構(gòu)的前提下可以添加作用于這些元素的新的操作,為數(shù)據(jù)結(jié)構(gòu)中的每個(gè)元素提供多種訪問(wèn)方式。它將對(duì)數(shù)據(jù)的操作與數(shù)據(jù)結(jié)構(gòu)進(jìn)行分離,是行為類模式中最復(fù)雜的一種模式。

訪問(wèn)者(Visitor)模式是一種對(duì)象行為型模式,其主要優(yōu)點(diǎn)如下。

  1. 擴(kuò)展性好。能夠在不修改對(duì)象結(jié)構(gòu)中的元素的情況下,為對(duì)象結(jié)構(gòu)中的元素添加新的功能。
  2. 復(fù)用性好??梢酝ㄟ^(guò)訪問(wèn)者來(lái)定義整個(gè)對(duì)象結(jié)構(gòu)通用的功能,從而提高系統(tǒng)的復(fù)用程度。
  3. 靈活性好。訪問(wèn)者模式將數(shù)據(jù)結(jié)構(gòu)與作用于結(jié)構(gòu)上的操作解耦,使得操作集合可相對(duì)自由地演化而不影響系統(tǒng)的數(shù)據(jù)結(jié)構(gòu)。
  4. 符合單一職責(zé)原則。訪問(wèn)者模式把相關(guān)的行為封裝在一起,構(gòu)成一個(gè)訪問(wèn)者,使每一個(gè)訪問(wèn)者的功能都比較單一。

訪問(wèn)者(Visitor)模式的主要缺點(diǎn)如下。

  1. 增加新的元素類很困難。在訪問(wèn)者模式中,每增加一個(gè)新的元素類,都要在每一個(gè)具體訪問(wèn)者類中增加相應(yīng)的具體操作,這違背了“開(kāi)閉原則”。
  2. 破壞封裝。訪問(wèn)者模式中具體元素對(duì)訪問(wèn)者公布細(xì)節(jié),這破壞了對(duì)象的封裝性。
  3. 違反了依賴倒置原則。訪問(wèn)者模式依賴了具體類,而沒(méi)有依賴抽象類。

模式的結(jié)構(gòu)與實(shí)現(xiàn)

訪問(wèn)者(Visitor)模式實(shí)現(xiàn)的關(guān)鍵是如何將作用于元素的操作分離出來(lái)封裝成獨(dú)立的類,其基本結(jié)構(gòu)與實(shí)現(xiàn)方法如下。

1. 模式的結(jié)構(gòu)

訪問(wèn)者模式包含以下主要角色。

  1. 抽象訪問(wèn)者(Visitor)角色:定義一個(gè)訪問(wèn)具體元素的接口,為每個(gè)具體元素類對(duì)應(yīng)一個(gè)訪問(wèn)操作 visit() ,該操作中的參數(shù)類型標(biāo)識(shí)了被訪問(wèn)的具體元素。
  2. 具體訪問(wèn)者(ConcreteVisitor)角色:實(shí)現(xiàn)抽象訪問(wèn)者角色中聲明的各個(gè)訪問(wèn)操作,確定訪問(wèn)者訪問(wèn)一個(gè)元素時(shí)該做什么。
  3. 抽象元素(Element)角色:聲明一個(gè)包含接受操作 accept() 的接口,被接受的訪問(wèn)者對(duì)象作為 accept() 方法的參數(shù)。
  4. 具體元素(ConcreteElement)角色:實(shí)現(xiàn)抽象元素角色提供的 accept() 操作,其方法體通常都是 visitor.visit(this) ,另外具體元素中可能還包含本身業(yè)務(wù)邏輯的相關(guān)操作。
  5. 對(duì)象結(jié)構(gòu)(Object Structure)角色:是一個(gè)包含元素角色的容器,提供讓訪問(wèn)者對(duì)象遍歷容器中的所有元素的方法,通常由 List、Set、Map 等聚合類實(shí)現(xiàn)。

其結(jié)構(gòu)圖如圖 1 所示。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容