Swift Design Pattern 系列教程 #3:外觀模式 (Facade) 與轉接器模式 (Adapter)


本教程是設計模式 (Design Patterns) 系列的第三部分,從本系列的第一篇文章開始,我們研究了「創建」類別的工廠方法模式 (Factory Method) 與單例模式 (Singleton) 兩個範例,第二篇文章則討論了「行為」類別的觀察者模式 (Observer) 與備忘錄模式 (Memento) 兩個範例。

在本次教學中,我將會解釋屬於「結構」類別的兩個範例:外觀模式 (Facade)轉接器模式 (Adapter)。我強烈建議你先閱讀前兩篇文章,以便熟悉軟體設計模式的概念。今天,除了簡單回顧設計模式的構成之外,我並不會再次複習先前提過的所有定義,所以你最好先回顧前兩篇文章。

讓我們在接下來幾節中,簡單回顧一下設計模式的一些定義。人稱「四人幫」(Gang of Four, GoF)的 Erich Gamma、Richard Helm、 Ralph Johnson 及 John Vlissides 所著的 “Design Patterns: Elements of Reusable Object-Oriented Software”,開創、收集、並解釋了目前常見的 23 種經典軟體開發設計模式。而我們今天將專注在外觀轉接器兩種模式的討論,它們屬於 GoF 所提出的「結構」類別。

協定導向程式碼與值語義

你可以找到許多附程式碼的設計模式教學,說明關於物件導向程式設計 (OOP)、參考語義 (reference semantics)、及參考型別(類別)。我正以協定導向程式設計 (POP)、值語義 (value semantics) 與數值型別 (structs) 為基礎,努力編寫一系列設計模式教學文章。如果你已經讀過我此系列教學的前兩篇文章,希望你參考我的建議,熟讀 POP 與 OOP、以及參考語義與值語義的觀念;如果還沒讀過,我強烈建議你去釐清這些概念。本次的教學只會圍繞 POP 與值語義的部分。

設計模式

設計模式是一個非常重要的工具,讓開發者可以管理複雜的程式碼。要將其概念化的話,可以說它是一種樣版技術,而每個樣版都是量身訂做來解決相對應、重複出現、又容易識別的問題。你可以把它們視作構思程式情境的最佳實作清單,在構思的過程中你會反覆查看它們。為了讓這個定義更清晰,想想看你在使用或編寫程式碼時,有多常遵循觀察者 (observer) 設計模式就會明白了。

在觀察者模式之中,主題實例通常會使用單一的關鍵資源,並將某種改變了的狀態透過廣播方式,通知其它同樣依賴這個資源的觀察者實例,感興趣的觀察者必須告訴主題實例它們有興趣收到通知,也就是說它們必須透過訂閱才可以接收通知。在 iOS 推播通知之中,使用者必須選擇接收,才會收到通知訊息;這就是觀察者一個很好的例子。

設計模式的種類

四人幫將 23 個設計模式分為「創建 (Creational)」、「行為 (Behavioral)」及「結構 (Structural)]」三大類別。本次的教學會討論結構類別的兩種設計模式。先定義「結構」這個詞:

「某些以明確的組織模式排列的東西」及 「實體在彼此關係中元素的總和」

- https://www.merriam-webster.com/dictionary/structure

結構類別設計模式旨在幫助你明確地定義每個程式碼片段的目的,並清楚地指定該段程式碼如何與其它程式碼進行交互。這類別的設計模式大部分都可以讓你簡化程式碼的使用,通常可以透過為程式碼創建一個易於閱讀的介面來做到。由於程式碼片段不會存在於真空之中,因此為程式碼片段提供一個良好的介面,就能夠明顯並清晰地定義每個程式碼片段之間的可能關係。

外觀設計模式 (Facade Design Pattern)

「外觀」一詞被定義為「任何給予特殊建築處理的建築物表面」及「虛偽的、表面的或人造的外表或效果」

- https://www.merriam-webster.com/dictionary/facade

在大多數的情況下,我們使用外觀模式為多個可能是多而複雜的介面,創造一個簡單的介面。你可能已經有創建過一般稱為「包裝器」的東西,用來為複雜的函式庫創造一個簡單的介面,目的是為了簡化函式庫的使用。

外觀設計模式的應用範例

我在 GitHub 上的外觀模式範例 playground 檔案,展現了這個模式如何為 iOS App 可獲得的沙盒檔案系統創造一個簡單的介面。iOS 檔案系統是一個很大的子作業系統,允許你創造、讀取、刪除、移動、重新命名、並複製檔案及路徑,也允許你獲取(有時候亦可設定)關於檔案和路徑的元數據,例如列出目錄中的文件、允許你檢查文件/目錄的狀態、決定檔案是否可寫入、提供預先定義好的 Apple 傾向路徑名稱等。請注意,實際上你可以做的事情,比我所列出的還要更多。

正因為 iOS 檔案系統是一個包含許多特性及功能的大議題,它是我們利用外觀模式來簡化使用的理想對象。外觀模式讓你省去不必要的功能,避免程式碼變得混亂。相反地,外觀模式也允許你指定特定 App 所需的功能;就我的情況而言,限制功能性為我最常使用到的功能,這使得我的外觀可以重用、擴展、並維護多個 App。

我使用協定導向程式設定 (POP) 與值語義,將 iOS 檔案系統主要的功能區分,並建構成可重用和擴展的單元:協定與協定擴展。

然後我將四個協議組成一個結構,表示所有iOS應用程序都可以使用的沙盒iOS目錄(另請參見此處)。 由於您可能越來越多地遇到POP和值語義的主題,請注意,組成的術語和組合在此是同義詞。

然後,我將四個協定組成一個結構,把一個可獲取的 iOS 沙盒目錄呈現給所有 iOS App(請參考這裡)。由於之後可能經常會遇到協定導向程式設定及值語義的議題,請注意組成 (composed) 與組合 (composition) 是同義詞。

請注意,下列程式碼的 Swift 錯誤處理和常見錯誤檢查只為說明用途;這樣一來,你可以更專注理解外觀模式的使用。

外觀模式的範例程式碼

讓我們來看看我的程式碼,請確認你有跟隨 GitHub 上的 playground 程式碼。這是一個預先定義目錄,列出了 Apple 希望你完成 App 大部分工作所在的預先定義目錄:

藉由限制我的檔案操作程式碼到這些已知的目錄下,我可以控制複雜性,同時簡化並保持在人機介面指南的規範中。

在查看我的檔案操作核心程式碼之前,讓我們先看看基於外觀設計模式的介面,因為這是本教學的主題。我創建了 iOSAppFileSystemDirectory 結構,作為一個簡單易讀的介面,可用於 AppDirectories 列舉中每個指定目錄的公共檔案系統功能中。沒錯,我是可以包含像是創建符號式的連結、或是使用 FileHandle 類別你獨立檔案進行精細操作等內容;但是,我從不使用這些功能,而且我希望保持精簡。

我已經創建了一個由四個協定組成的外觀 (我知道你在下面程式碼只看到三個,不過其中一個是經由繼承而來) :

這裡有一些用來測試 iOSAppFileSystemDirectory 結構的程式碼:

這裡是我執行上述程式碼片段後,在終端機顯示的輸出:

讓我們簡單討論一下剛剛用來組成 iOSAppFileSystemDirectory 的協定。我們用了 AppDirectoryNames 協定與協定擴展,來將 URL 類型的完整路徑,劃分為我在 AppDirectories 列舉中指定的 Apple 預先定義目錄:

AppFileStatusChecking 是我的協定及協定擴展,用來封裝我的 AppDirectories 列舉中有關存儲在目錄中的文件狀態數據。「狀態」是指確定文件是否存在、是否可寫等。

AppFileSystemMetaData 是我的協定及協定擴展,用來區分列出的目錄內容,並獲取副檔名屬性,兩者都來自 AppDirectories 列舉裡的目錄:

最後,AppFileManipulation 協定與協定擴展,是用來封裝位於 AppDirectories 列舉中指定目錄中檔案的讀取、寫入、刪除、重新命名、移動、複製、及改變副檔名等資訊。

轉接器設計模式 (Adapter Design Pattern)

「改動 (adapt)」一詞被定義為「透過修改來使其適合(用於新用途)」

- https://www.merriam-webster.com/dictionary/adapts

「轉接器 (adapter)」一詞被定義為 「調整使裝置適用於非原本用途的附件」

- https://www.merriam-webster.com/dictionary/adapter

轉接器模式是用來讓現有的程式碼(暫稱為「A」),在不需修改原本「A」的程式碼條件下,與其他可能不完全相容於「A」的程式碼(稱為「B」)一起運作。我們可以創造某種類型的轉接器,使「A」和「B」儘管有差異也可以一起運行。請記住,原本「A」的程式碼不能被修改(不論是因為會破壞程式碼、或是因為我們沒有原始碼)。

轉接器設計模式的應用範例

我在 GitHub 的轉接器範例 playground,展示了我們如何以 iOS 檔案系統作為基礎,來討論及設計轉接器設計模式的範例。假設我們從前文部分獲得了我的 iOS 檔案系統程式碼,其中目錄和文件的所有路徑都表示為 URL 實例。考慮一種情況,我們已經獲得大量的程式碼來操作 iOS 檔案系統,但是目錄和文件的所有路徑都是用字串的實例表示,並且必須使基於 URL 的程式碼可以與以字串為基礎的程式碼一起運行。

轉接器模式的範例程式碼

讓我們看看我的程式碼。請確認你有跟著我在 GitHub 上的 playground 程式碼。為了專注在轉接器模式上,我們將會使用我的 AppDirectories 列舉和 AppDirectoryNames 協定與協定擴展刪減後的版本:

我們能夠使用的一個技巧,就是創造一個「專用」轉接器,可以給我們提供基於字串的路徑到 AppDirectories 的目錄之中,也可以給我們基於字串的路徑到儲存在 AppDirectories 之中的檔案。

以下是用來測試 iOSFile「專用」轉接器的程式碼,請注意我的行內註解:

以下是 playground 檔案中的逐行註解,出現於每行程式碼同一行的最右邊,代表運行時的程式碼數值,與先前的程式碼片段互相對應。下方的註解會一對一的對應到上方的程式碼:

我偏好的技巧是設計一個轉接器協定,讓我基於字串路徑的程式碼能夠遵從,那麼它就可以使用 String 路徑,而非 URL 路徑。

以下是ㄧ些用來測試 AppDirectoryAndFileStringPathNames 結構的程式碼,並且採用 AppDirectoryAndFileStringPathNamesAdpater 轉接器協定(繼承自 AppDirectoryNames 協定)。請注意兩行行內註解:

就像剛剛一樣,以下是 playground 檔案中的逐行註解,出現在每行程式碼同一行的最右邊,代表運行時的程式碼數值,對應到先前的程式碼片段。下方的註解會一對一的對應到上方的程式碼:

結論

設計模式不但鼓勵程式碼的重用,還可以幫助你提高你程式碼的一致性、可讀性、低耦合性、可維護性、以及擴展性。當你識別出 App 中重複出現而通用的特性,我鼓勵你採用基於設計模式的程式碼,並將它放入框架之中,這樣一來,你便可以重複使用程式碼。

再次感謝你閱讀這系列的教學。享受工作,持續學習,我們下次再會!

譯者簡介:HengJay,iOS 初學者,閒暇之餘習慣透過線上 MOOC 資源學習新的技術,喜歡 Swift 平易近人的語法也喜歡狗狗,目前參與生醫領域相關應用的 App 開發,希望分享文章的同時也能持續精進自己的基礎。

LinkedIn: https://www.linkedin.com/in/hengjiewang/
Facebook: https://www.facebook.com/hengjie.wang

原文Design Patterns in Swift #3: Facade and Adapter


熱愛寫作的多產作家,亦是軟體工程師、設計師、和開發員。最近專注於 Objective-C 和 Swift 的 iOS 手機 App 開發。但對於 C#、C++、.NET、JavaScript、HTML、CSS、jQuery、SQL Server、MySQL、Oracle、Agile、Test Driven Development、Git、Continuous Integration、Responsive Web Design 等。

blog comments powered by Disqus
訂閲電子報

訂閲電子報

AppCoda致力於發佈優質iOS程式教學,你不必每天上站,輸入你的電子郵件地址訂閱網站的最新教學文章。每當有新文章發佈,我們會使用電子郵件通知你。

已收你的指示。請你檢查你的電郵,我們已寄出一封認證信,點擊信中鏈結才算完成訂閱。

Shares
Share This