設計模式問答2
介紹
這是設計模式問答1的連載。在這個系列,我們將覆蓋到解釋器、迭代器、調停者、備忘錄和觀察者模式。
如果你還沒有閱讀過我之前的系列,你可以隨時從下面開始
- 設計模式問答1:工廠模式,抽象工廠模式,構造者模式,原型模式,單例模式,命令模式
- 設計模式問答3:狀態模式,策略模式,訪問者模式,適配器模式,享元模式
- 設計模式問答4:橋接模式,組合模式,裝飾者模式,外觀模式,職責鏈模式(COR),代理模式,模板模式
- UML問答1:UML部分1
- UML問答2:UML部分2
什么是解釋器模式?
解釋器模式允許我們將語法解釋為代碼解決方案。好了,這意味著什么?語法會被映射到類,并形成解決方案。 舉個例子,7 – 2能夠被映射到“clsMinus”類。一句話,解釋器模式為我們提供了一種解決方案,這個方案指導如何編寫一個解析語法并執行代碼的解釋器。下面是一個 解釋器的簡單例子,它能夠按照我們提供的日期格式語法,將日期解釋為對應的代碼,并輸入正確的結果。
讓我們開始做圖“日期語法”中所示的日期格式的解釋器。在開始之前,我們要先理解一下解釋器模式中不同的組件,然后再來處理映射。上下文部分包含數據,而邏輯部分包含將上下文中數據轉換成可讀格式的轉換邏輯。
讓我們看一下日期格式的語法是怎么定義的。定義任何語法的第一步,是把語法分解成小的邏輯組件。圖“語法映射與類 的映射”展示了怎么識別這些組件,以及怎么映射到處理這部分語法的邏輯類上面。我們已經把日期格式打斷成了4個組件,分別是月、日、年和分隔符。對這4個 組件,我們將分別定義包含圖中展示的邏輯的類。然后,我們將為日期格式的不同組件創建不同的類。
前 面說過,有兩種類,一種是包含邏輯的表達式類,另一種是包含數據的上下文類,如圖“表達式和上下文類”中所示。我們定義了不同類中的表達式解析算法,這些 類都從公共接口“ClsAbstractExpression”派生,并實現了“Evaluate”方法。“Evaluate”方法接收包含數據的上下文 類作為參數;它根據表達式邏輯來解析數據。“ClsYearExpression”實例將“YYYY”替換成年份值,而 “ClsMonthExpression”將“MM”替換成月份值,以此類推。
現在,我們有了單獨的表達式解析邏輯類,然后我們來看看客戶端會如何使用這個邏輯。客戶端首先把日期語法格式傳遞給 上下文類。依據日期格式,我們依次向集合中添加表達式實例。如果我們找到了“DD”,我們就添加一個“ClsDayExpression”實例;如果我們 找到了“MM”,就添加一個“ClsMonthExpression”實例,等等。最后,我們只需要遍歷集合,并調用“Evaluate”函數。所有的 “Evaluate”函數執行完之后,我們就顯示結果。
你能解釋迭代器模式嗎?
迭代器模式允許在不暴露內部代碼實現的情況下,順序訪問每個元素。讓我們來理解一下。假設你有一個記錄集合,你需要順序遍歷 每條記錄,并且需要保持當前訪問的位置,那么你需要的正是迭代器模式。這是最普通的設計模式,你會在不知不覺中用到它。在某些程度上,當你使用 “foreach”(它允許我們逐個元素地訪問一個集合)時,你就已經在使用迭代器模式了。
在圖“迭代器業務邏輯”中,我們使用 “clsIterator”類來存放顧客類的集合。我們在“clsIterator”類內部,定義了一個數組,和一個名稱為“FillObjects”的 方法,這個方法用來加載數組的內容。顧客集合數組是私有的,顧客的數據可以通過數組的下標來訪問。因此我們定義了一組公有函數,包含 “getByIndex”(通過指定下表來訪問),“Prev”(得到集合中前一個顧客數據),“Next”(得到集合中下一個顧客數 據),“getFirst”(得到集合中第一個顧客數據),“getLast”(得到集合中最后一個顧客數據)。
我們只對客戶端暴露這些函數。這些函數小心的順序遍歷集合,并能夠記憶當前遍歷的索引。
下面的圖“客戶遍歷邏輯”說明了該如何使用類“clsIterator”的實例“ObjIterator”,顯示下一個、前一個、最后一個、第一個,以及通過索引顯示顧客數據。
你能解釋調停者模式嗎?
大多數時候,項目中組件間的通訊都很復雜。因此組件間的邏輯關系也變得異常復雜。調停者模式幫助對象間用不互相關聯的方式來通訊,從而使復雜度最小化。
讓我們考慮圖“調停者模式示例”,它描述了一個需要使用調停者模式的真實場景。它是一個非常用戶友好的接口。它有3個典型的場景。
場景1:當用戶在文本框中輸入時,應該使添加和清除按鈕可用。一旦當文本框中沒有文字時,應該禁用添加和清除按鈕。
場景2:當用戶點擊添加按鈕時,文本框內的數據應該被輸入到列表框內。一旦數據被輸入到列表框,它應該清空文本框的內容,并禁用添加和清除按鈕。
場景3:- 當用戶點擊清除按鈕時,名字文本框內的內容被清空,并禁用添加和清除按鈕。
現在從界面上來看上面幾個場景,我們可以推斷這些界面之間的交互是多么復雜。下圖“組件間復雜交互”顯示出了邏輯復雜性。
好了,讓我給你們看一個好看的圖,即下圖“通過調停者簡化”。與其組件之間直接通訊,不如通過一個作為調停者的中心組件通訊,調停者組件管理著發送給其他組件的消息,這樣邏輯更加優雅和清晰。
現在我們來看看代碼會是什么樣子。我們將使用C#,但是你可以很輕松的把這種思想應用在Java或其他語言上。下圖“調停者類”展示了一個調停者類完整的代碼概述。
調停者類做的第一件事,是保存擁有復雜通訊的類的引用。因此,我們 對外暴露了3個重載的方法“Register”。“Register”方法接收文本框對象和按鈕對象為參數。交互場景集中在 “ClickAddButton”,“TextChange”和“ClickClearButton”三個方法上。將根據場景不同,這些方法將管理UI組 件的可用與禁用。
現在的客戶邏輯非常優雅、非常酷。在構造函數中,我們首先將 參與復雜通訊的所有組件注冊到調停者對象中。然后在每個場景中,我們只需要調用調停者對象的函數。簡單地說,當有文本變化時,我們調用調停者對象的 “TextChange”函數;當用戶點擊添加按鈕時,我們調用“ClickAddButton”;當點擊清除按鈕時,調用 “ClickClearButton”函數。
你能解釋備忘錄模式嗎?
備忘錄模式能夠在不破壞封裝原則的前提下,獲取對象內部狀態。備忘錄模式幫助我們存儲一個對象的快照,它可 以在任意時間被恢復。讓我們通過實例來理解。考慮圖“備忘錄示例”,它展示了一個顧客的界面。假設用戶開始編輯一條顧客記錄,并做了一些修改。然后用戶覺 得有錯誤,希望能夠恢復到原始的數據。這時備忘錄模式就登場了。它幫助我們存儲數據的一個備份,并且當用戶點擊“取消”按鈕時,對象能夠恢復到它的原始狀 態。
讓我們嘗試用C#來實現剛才所講的顧客界面。下圖是顧客類 “clsCustomer”,它聚合了一個備忘錄類“clsCustomerMemento”。備忘錄類將保存數據的快照,它是顧客類 “clsCustomer”的精確的復制品(除了方法)。當顧客類“clsCustomer”初始化時,備忘錄類也將被初始化。當顧客類數據變化時,備忘 錄類的快照不變化。“Revert”函數把備忘錄的數據寫回到主類。
客戶端的代碼相當簡單。我們創建一個顧客類。一旦遇到問題,我們點擊“取消”按鈕,調用“Revert”函數,將修改過的數據恢復到備忘錄快照的原始數據。圖“備忘錄客戶端代碼”形象地展示了這個過程。
你能解釋觀察者模式嗎?
觀察者模式幫助我們與父類,關聯類或者依賴類之間進行通訊。觀察者模式中,有兩個重要的概念,分別是“主體”和“觀察者”。主體發 送通知,如果觀察者已經注冊到主體的話,觀察者會收到通知。下圖“主體和觀察者”展示了應用程序(主體)是如何給所有觀察者(郵件,事件日志,短消息服 務)發送通知的。你可以把這個例子對應到發布者與訂閱者模型。發布者就是應用程序,而訂閱者是郵件,事件日志和短消息服務。
讓我們嘗試對前面定義的示例進行編碼。 首先我們看一下訂閱者/通知者類。圖“訂閱者類”做了一個直觀地展示。對所有的訂閱者,我們有一個公共的接口,“INotification”,它有一個 “notify”方法。所有需要接收通知的類,都需要實現這個“INotification”接口。所有需要接收通知的類,定義各自的響應方法。對當前場 景,我們只打印一個消息,表明特定的通知被執行了。
前面說過,觀察者模式中,有兩個部分,一個是我們前面說過的觀察者/訂閱者,另一個就是發布者,或者叫主體。
發布者有一個所有對接收通知感興趣的訂閱者的集合列表。通過“addNotification”和“removeNotification”,我們可以在列表中增加或者刪除訂閱者。“NotifyAll”方法遍歷所有的訂閱者,并發送通知。
現在,我們已經有了發布者和訂閱者類。我們來動手編寫一下客戶端代碼。下面是觀察者模式客戶端的代碼片段。首先我們創建一個擁有訂閱者集合的通知者對象。然后我們向集合中添加需要被通知的訂閱者。
現在,如果客戶端輸入的顧客代碼超過10個字符,就需要通知所有的訂閱者。
如果你沒有學習過設計模式,或者不愿完全閱讀本文,請收看我們的免費視頻設計模式培訓和問答。
許可
本文,以及相關的代碼和文件,通過 The Code Project Open License (CPOL) 協議授權。
原文鏈接: codeproject 翻譯: ImportNew.com - shenggordon
譯文鏈接: http://www.importnew.com/14347.html