【Design Patterns】Decorator 實作與應用

| 前言

前陣子開發了三支產 Word 文件的功能,一開始看了一下範本跟資料,很直覺的把整份文件作為一個範本,再把資料塞進該範本產出最後的文件,不過後來使用者需求調整了好幾次,所以又在這三支功能根據各自需求加上頁碼、空白頁、封面、合併內容 … 等,經歷這幾次的調整後,不知道下一次使用者又要加什麼,有鑑於此,應該要想個好辦法替需求變動留一點彈性,這時就該派上 Decorator Pattern 來預留一下後路。

| 需求簡述

先看一下目前需求調整後的文件的樣子(ps.需求調整後只需要產兩份Word文件)

[word文件 1]

[word文件 2] 

簡單說明需求重點:

[word文件1]會有封面頁面、出席人員頁面、會議記錄頁面,其中會議記錄頁面需要有頁碼。

[word文件1]會有兩個封面頁面(分別在第一頁與最後一頁)、空白頁面、附件頁面,其中附件頁面需要有頁碼。

| 目的

說一下為什麼要套用 Decorator 來實作這個列印 Word 功能,上面有提到使用者需求變動大,若沒有使用 Decorator 的寫法大概會是像下面這樣 (以 Word 文件 2 為例)

這種寫法在一個大方法做太多事情,若經過一段時間又要回來需求調整,勢必要整個程式碼重新 審視與理解,另一方面若大方法內有通用區域變數,調整需求時有可能改 A 影響到 B 的情況發生,有些人可能會覺得把這個大方法抽成多個小方法,但他們還是在同一個Class內,且架構不清晰,時間久了容易忘記他們是如何交互作用的,再者,若其中一些方法會用到其他lib,以相依性來看是整個Class都相依於該lib,相依性不夠精準,對於未來重構或是Debug來說可能會是一些負擔。

所以用 Decorator 來實作可以有效隔離程式碼,提高內聚,架構清晰,重用一些相同功能以及組合不同物件產生不同文件以因應未來的需求變化。

| Decorator Pattern 回顧

簡單回顧一下Decorator pattern 的類別圖,主要有兩個角色,一個「基本元件(ConcreteComponent)」與「裝飾者(Decorator)」,他們都是一個「元件(Component)」,所以裝飾者可以在這些元件的操作前後做一點事情來組合出最終的元件。

 

| 分析需求

將上述Word文件所找出的「基本元件」與「裝飾者」如下圖

[word文件1]

[word文件2]

[額外需要的功能]

其中紅色為基本元件,藍色為裝飾者,統計與解釋各個元件功用如下

基本元件(ConrectComponent)

  • 封面
  • 出席人員
  • 會議記錄
  • 附件

裝飾者(Decorator)

  • 頁碼
    • 頁面需要有頁碼
  • 空白頁
    • 某些Word文件需穿插空白頁
  • 區塊設定
    • 頁碼只侷限在某些頁面
  • 合併
    • 多份文件需合併成一份文件

| 實作

根據上述分析結果,程式的類別圖,如下圖

接著各個基本元件只需專注產生原始資料,裝飾者只要透過傳進來的Document物件專注處理自己的事務,處理完成後再回傳出去即可,最終產生一份文件的程式碼如下方所示。

ps. 各元件如何實作不是本篇重點,故只列出怎麼透過元件組合,來產出最終的 Word 文件

| 結論

我們採用了 Decorator Pattern 來實作產生 Word 文件功能,提高程式碼的內聚,使得未來需求變更時可以更快速且穩固的調整需求,最後列出一些優缺點分析。

優點:

  • 組合不同物件產生不同Word文件,有機會因應需求變更
  • 需求新增只需要專注在新增的程式碼上面,不必修改或重新審視不相關的程式碼
  • 內聚高,每個class只專心做自己要做的事情
  • 可透過外部設定(config、XML、DB)動態組合物件產生Word文件

缺點:

  • 組合多,要注意是否物件切得夠乾淨(例如:先後順序),否則可能會有非預期情況或錯誤發生

【Design Patterns】Decorator 實作與應用 有 “ 1 則迴響 ”

  1. 看了Design Pattern 是一回事,能把Pattern 套用在實際專案中是另一回事。
    在實作的過程中應該會有更深的體會。

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *