iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 3
0
自我挑戰組

來讀設計模式:Junior developer 跟大家一起練功系列 第 14

DAY14: Abstract Factory 模式

今天我們介紹 abstract factory 模式。本篇包括:

  • 簡介此模式
  • 舉例說明
  • 此模式的關鍵特徵描述

簡單來說:abstract factory 模式

GoF 如此說:

(Abstract factory 模式)為建立一組相關或相依的物件提供一個介面,而且無須指定它們的具體類別。

也就是透過一種協調的方式來讓特定的物件實體化。我們來看個例子吧!

舉個例子

一個能顯示與列印形狀的系統

這個例子是一個電腦系統(ApControl):顯示並列印出取自資料庫的幾何形狀。Here's the kicker: 系統必須要能判斷出裝置的配置優劣,而選擇出適合的驅動程式:低配置與高配置兩種。如下表。

驅動功能 低配置裝置下 高配置裝置下
顯示 LRDD低解析度顯示驅動程式 HRDD低解析度列印驅動程式
列印 LRPD高解析度顯示驅動程式 HRPD高解析度列印驅動程式

在本例中,低解析度顯示驅動程式必然配上低解析度列印驅動程式,反之亦然。但並非實際狀況都是如此。

有什麼解決方案?

1. Switch-case

最直覺的做法是用 switch-case 再 ApControl 中直接做判斷。但想單然爾這樣做是不好的:這麼做會讓驅動程式的規則實際使用混雜在一起。如此會造成:

  • 緊耦合
  • 低內聚

這將會增加往後的維護成本,而且遇到需求變化時,無法靈活調整。

2. 特殊化繼承

第二種方案是依照配置高低,再將 ApControl 向下衍生 LowResApControlHighResApControl。(此時,ApControl 為抽象類別)。

這個方案也會有一些我們之前碰到的問題:新需求產生時,衍生類別可能爆炸增長

3. 將變化封裝

當然,我們要用物件導向的一些基本原則去解決。我們知道,在這個案例中,在變化的是顯示驅動程式列印驅動程式。於是我們將其抽出,然後封裝;之後再讓 ApControl 去使用它們(用聚合取代繼承)。

我們會得到下面的 UML 圖:

配上相對應的程式碼(尚未齊全):

class ApControl {
  	private myDisplayDriver;
  	private myPrintDriver;
  	public doDraw() {
      	// ...
      	// 讓 myDisplayDriver 對應到正確的顯示驅動程式
      	myDisplayDriver.draw();
    }
  
  	public doPrint() {
      	// ...
      	// 讓 myPrintDriver 對應到正確的列印驅動程式
      	myPrintDriver.print();
    }
}

我們接下來必須思考的就是程式碼裡頭的 comment:該如何建立合適的物件?

Abstract Factory模式:工廠物件負責生產物件

這裡的辦法就是藉由一個工廠物件 ResFactory 來協助驅動程式的建立。如此做有幾個好處:

  • 生產與追蹤物件的職責委任給工廠,這讓 ApControl 只要關注在如何使用合適的選擇驅動程式上。如此可達到(使用規則與實際使用的)職責分解
  • 加強內聚性:工廠負責生產適合的驅動程式,ApControl 負責使用驅動程式。

我們知道 ResFactory 需要做兩件事:生產應該使用的顯示驅動程式生產應該使用的列印驅動程式。因此,我們會衍生出 LowResFactoryHighResFactory。看下圖就能理解。

將上圖配合原本的系統,結合起來就是我們將 abstract factory 模式實踐在此例上的 UML 圖:

當然,你也許也會注意到 LRDDHRDD 不一定會共享同一個介面, LRPDHRPD 也有一樣的問題。但是我們知道我們總是可以用 adapter 模式解決這個問題,所以我們就不再贅述。

有些問題仍然還在?

ApControl 仍是需要以某些方式選擇適合的工廠產生適合的物件,這意指我們可能仍會有 switch-case 做一些判斷。

當然,要有判斷是一定的,但是此處的 switch-case 與 解決方案1 的 switch-case 還是有明顯的差異,差別就在於我們將某些職責抽離給工廠負責,這讓這裡的判斷句沒有讓規則與實際使用互相耦合,反倒是將其解耦了。

我們也可以增加一些相關的 config 檔,使主程式能夠較輕鬆地能取得正確的工廠物件。

關鍵特徵

以下是本模式的關鍵特徵:

項目 內容
意圖 需要為特定的客戶或情況提供物件(或物件組)
問題 需要實體化一組相關的物件
解決方案 協調物件組的建立
參與者與實作者 AbstractFactory 為如何建立物件組每個成員定義介面。每個組都由 ConcreteFactory 進行建立
效果 使用哪些物件如何使用那些物件的邏輯分開
實作 定義一個抽象類別來指定建立哪些物件,為每個組實作具體類別

以下是 UML 圖:

接下來

下一篇我們將講到本書的 Chapter 14:設計模式的原則與策略。See ya!


上一篇
DAY13: Bridge 模式2
下一篇
DAY15: 使用設計模式的原則與策略
系列文
來讀設計模式:Junior developer 跟大家一起練功22

尚未有邦友留言

立即登入留言