設(shè)計模式問答(二)

什么是解釋器模式?

解釋器模式允許將語義解釋到代碼解決方案中。下面讓我們來了解具體含義?語法是映射到類中來應(yīng)用到解決方案中,例如7-2可以映射到‘clsMinus’類。在第一行解釋器模式給我們提供了一種如何編寫解釋器的方案,該方案中解釋器可以讀取并在代碼中執(zhí)行該語法。例如以下例子中我們給出了一個日期格式化語法,其中解釋器給出了轉(zhuǎn)換的代碼解決方案并輸出期望結(jié)果。

圖1:日期語法

讓我們來寫一個如圖“日期語法”中所示的日期格式化解釋器。在開始之前我們需要理解解釋器模式中不同組件,之后我們將映射相同的語法。Context包含數(shù)據(jù),邏輯部分包含將context轉(zhuǎn)換為可讀格式的邏輯。

圖2:上下文及邏輯

接下來我們看看日期格式化中的語法。在定義任何語法之前,我們首先要把語法拆分為小的邏輯塊。圖“語法映射到類”展示了不同組件是如何確定的,然后映射到類的語法只是實現(xiàn)了完整語法中的一部分。這樣我們將日期格式化拆分為4個組件月、日、年以及分隔符;對這四個組件我們定義了不同的類來包含各自的邏輯,如圖“語法映射到類”中所示。因此,我們將為日期格式化中4個不同的組件創(chuàng)建不同的類。

圖3:語法映射到類

如前所述,有兩個類其中一個是表達式類,該類包含邏輯;另一個是上下文類(context),該類包含數(shù)據(jù),如圖“表達式和上下文類”所示。我們將不同的解析定義在不同的類中,所有這些類都繼承自‘ClsAbstractExpression’接口,該接口包含‘Evaluate’方法;其中‘Evaluate’方法持有一個包含有數(shù)據(jù)的上下文類,并且通過表達式邏輯來解析數(shù)據(jù)。例如‘ClsYearExpression’會將‘YYYY’替換為年份,’ClsMonthExpression’將‘MM’替換為月份等等。

圖4:解釋器模式類圖

圖5:表達式和上下文類

至此我們已經(jīng)將表達式解析邏輯封裝在了不同的類中,現(xiàn)在我們來看下客戶端使用邏輯??蛻舳耸紫葌鬟f日期格式化語法到上下文類;根據(jù)日期格式化我們添加表示類到一個集合中。例如當發(fā)現(xiàn)“DD”表達式時會增加‘ClsDayExpression’,遇到‘MM’表達式時則會添加‘ClsMonthExpression’類,等等。最終我們循環(huán)調(diào)用這些類的‘Evaluate’方法。當所有類都被調(diào)用后我們就得到了顯示結(jié)果。

圖6:客戶端效用解釋器代碼邏輯

什么是迭代器模式?

迭代器模式允許在不暴露內(nèi)部代碼的情況下順序存取元素。具體含義如下。假設(shè)有一個記錄的集合,我們想順序性瀏覽這些結(jié)合同時還想保存當前已經(jīng)瀏覽的記錄的位置,那么迭代器模式就是我們所需要的。該模式是最常見且經(jīng)常無意中使用的模式。在所有使用‘foreach’(允許順序性循環(huán)一個集合)循環(huán)的地方我們就在一定程度上在使用迭代器模式了。

圖7:迭代器模式業(yè)務(wù)邏輯

在圖“迭代器模式業(yè)務(wù)邏輯”中我們定義了一個包含客戶類集合的類‘clsIterator’。因此我們我們首先在‘clsIterator’中定義了一個ArrayList以及一個‘FillObjects’方法來為ArrayList填充數(shù)據(jù)。這個消費者集合ArrayList是私有的(private),并且客戶數(shù)據(jù)可以通過集合中的下標來查找。因此我們開放了‘getByIndex’方法(通過指定下標來查找元素)、‘Prev’方法(獲得集合中前一個元素)、‘Next’方法(獲取集合中下一個元素)、‘getFirst’方法(獲取結(jié)合中第一個元素)、‘getLast’方法(獲取集合中最后一個元素)。

客戶端只能看到這些方法,通過這些函數(shù)來順序訪問集合并且可以記住已經(jīng)訪問過的下標。

下圖“客戶端使用迭代器模式邏輯”展示了如何使用‘clsIterator’中next、previous、last、first函數(shù)以及指定下標來創(chuàng)建‘ObjIterator’對象的。

圖8:客戶端使用迭代器模式邏輯

什么是調(diào)停者模式?

很多時候項目中組件之間的通信是非常復(fù)雜的,因為組件之間的相互調(diào)用而變的復(fù)雜。調(diào)停者模式幫助對象以沒有關(guān)聯(lián)的方式來通信,這使得復(fù)雜度最小化。

圖9:調(diào)停者模式簡單例子

讓我們考慮上圖“調(diào)停者模式簡單例子”中的情況,該圖描述了一個真實場景中需要調(diào)停者模式的情況。它是一個非常用戶友好的接口;它有3中典型場景:

場景1:當用戶在文本框中輸入文字后需要開啟增加按鈕以及清空按鈕。在這個例子中文本框中為空,此時需要禁用增加和清空按鈕。

圖10:場景1

場景2:當用戶點擊添加按鈕時數(shù)據(jù)應(yīng)該進入到列表框中。一旦輸入內(nèi)容進入列表框,系統(tǒng)需要清空文本框并禁用增加和清除按鈕。

圖11:場景2

場景3:如果用戶點擊清除按鈕系統(tǒng)應(yīng)該清空文本框并禁用增加和清除按鈕。

圖12:場景3

從上圖場景3中UI界面可以看出這些UI之間的交互是多么復(fù)雜。下圖“組件之間的復(fù)雜交互”展示了邏輯的復(fù)雜性。

圖13:組件之間的復(fù)雜交互

接著我們來看下圖“調(diào)停者使用簡化圖”。各個組件之間并不是直接交互,而是都與一個中心化組件如調(diào)停者交互,由調(diào)停者來關(guān)注發(fā)送信息至其他組件,這樣邏輯將變的干凈清晰。

圖14:調(diào)停者使用簡化圖

下面我們看看代碼是如何寫的。我們將使用C#但是大家也可以很容易的將其轉(zhuǎn)換為java代碼或者其他語言。下圖“調(diào)停者類”展示的調(diào)停者類的完整代碼。

調(diào)停者類首先做的事情就是持有復(fù)雜交互相關(guān)類的引用。所以這里我們開放3個名為‘Register’的重載方法;‘Register’持有文本框?qū)ο蠛桶粹o對象;場景的交互被集中在‘ClickAddButton’、’TextChange’、‘ClickClearButton’方法中,這些方法將根據(jù)不同場景啟用和禁用相關(guān)UI。

圖15:調(diào)停者類

同時客戶端代碼非常整潔。在構(gòu)造函數(shù)中首先注冊所有需要和調(diào)停者類交互的組件;接著對于每個場景只需要調(diào)用調(diào)停者類的方法即可。簡而言之,當文本發(fā)生變化時我們調(diào)用調(diào)停者類的‘TextChange’方法,當用戶點擊添加按鈕時調(diào)用‘ClickAddButton’方法,當用戶點擊清空按鈕時調(diào)用‘ClickClearButton’方法。

圖16:調(diào)停者模式客戶端邏輯

什么是備忘錄模式?

備忘錄模式是提供一種在不破壞封裝的情況下提供捕獲對象內(nèi)部狀態(tài)的方式。它有助于存儲對象的快照以便任何時候查詢。下面我們來了解它在實際場景中的含義。下圖“備忘錄模式實踐例子”中展示的了一個客戶屏幕;假設(shè)用戶開始編輯一條客戶記錄并作出一些修改;過了一會以后該用戶覺得操作有誤想要恢復(fù)到原始數(shù)據(jù),這就是備忘錄模式的適用場景。它幫助我們存儲一份數(shù)據(jù)的拷貝并在用戶點擊取消時恢復(fù)到原始狀態(tài)。

圖17:備忘錄模式實踐例子

下面我們來嘗試使用C#完成我們剛剛討論的客戶UI的例子。以下是客戶類‘clsCustomer’,該類聚合了存儲數(shù)據(jù)快照的備忘錄類‘clsCustomerMemento’。其中備忘錄類‘clsCustomerMemento’是明確用來復(fù)制客戶類(不包括方法)‘clsCustomer’;當消費者類‘clsCustomer’初始化時,備忘錄類也跟著初始化;當客戶類數(shù)據(jù)發(fā)生改變時,備忘錄類中的快照未發(fā)生變化;其中‘Revert’方法可以將備忘錄中的數(shù)據(jù)恢復(fù)到main類中。

圖18:備忘錄中的客戶類

客戶端代碼非常簡單。創(chuàng)建客戶類,一旦有問題我們可以點擊取消按鈕來調(diào)用‘revert’方法將數(shù)據(jù)恢復(fù)至備忘錄中的快照數(shù)據(jù)。圖“備忘錄客戶端代碼”圖中展示了該邏輯。

圖19:備忘錄客戶端代碼

什么是觀察者模式?

觀察者模式幫助我們完成父類和與他相關(guān)或依賴的類之間的通信。其中有兩個比較重要的概念‘Subject’(主題)和‘Observers’(觀察者)。主題(subject)發(fā)送通知后,如果觀察者注冊到該主題,則會收到通知。下圖“subject和observers”展示了應(yīng)用(主題)發(fā)送通知到所有觀察者(email、事件日志、SMS)。我們也可以將這個例子封裝成發(fā)布/訂閱模型;發(fā)布者就是應(yīng)用,訂閱者就是email、事件日志、sms。

圖20:subject和observers

接下來我們嘗試我們在前一小節(jié)中定義的簡單示例代碼。首先,我們先看看訂閱者/通知類。圖“訂閱者類”展示了該代碼,其中為所有訂閱者定義了統(tǒng)一接口如‘INotification’,該接口有一個‘notify’方法。這個接口被所有具體的通知類實現(xiàn),所有通知類定義自己的通知方法。對當前的場景來說我們只是顯示一個打印字符串來表示某個特定方法被執(zhí)行。

圖21:訂閱者類

如前所示,觀察者模式中分為兩個部分,一個是觀察者/訂閱者,如前邊我們提到的一樣;另一個是發(fā)布者或者主題。

發(fā)布者有一個集合來存放所有需要收到通知的訂閱者。通過使用‘a(chǎn)ddNotification’和‘removeNotification’可以從集合中增加或刪除訂閱者;使用‘NotifyAll’方法循環(huán)所有訂閱者來發(fā)送通知。

圖22:發(fā)布者/主題類

至此我們已經(jīng)了解了發(fā)布致和訂閱者類,下面讓我們來些客戶端代碼并看看觀察者表現(xiàn)。下面是一個觀察者客戶機代碼片段。首先創(chuàng)建一個擁有訂閱者集合的通知者對象,接著將需要被通知的訂閱者添加到結(jié)合中。此時的邏輯是如果客戶的代碼長度超過10個字符時通知所有訂閱者。

圖24:觀察者模式客戶端代碼

1. 本文由程序員學(xué)架構(gòu)翻譯

2. 本文譯自

http://www.codeproject.com/Articles/28359/Design-pattern-FAQ-Part-Design-pattern-training

3. 轉(zhuǎn)載請務(wù)必注明本文出自:程序員學(xué)架構(gòu)(微信號:archleaner)

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

相關(guān)閱讀更多精彩內(nèi)容

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