創建模式 – 3 個 PK | Factory vs Builder vs Abstract Factory | 最佳實踐

創建模式 – 3 個 PK | Factory vs Builder vs Abstract Factory | 最佳實踐

Overview of Content

創建模式是軟件設計中重要的概念,它包括工廠模式、抽象工廠模式、建造者模式、單例模式以及原型模式等。這些模式提供了在應用程序中創建和管理物件的不同方式。然而,其中 工廠模式、抽象工廠模式和建造者模式 的概念常常讓人感到困惑,因此本文將深入探討這些模式的原理、應用和最佳實踐。

工廠模式旨在將物件的創建邏輯封裝在一個單獨的類中,從而使客戶端代碼與具體類的創建過程解耦。將探討工廠模式的整體概念,以及如何在不同情境下有效地應用它。

抽象工廠模式提供了一種創建相關或相互依賴物件家族的方式,而無需指定其具體類。將探討抽象工廠模式的一系列抽象產品以及如何利用這種模式來實現高內聚、低耦合的設計。

建造者模式則允許客戶端代碼根據特定的步驟和組合來創建物件,從而可以構建出不同結構的物件。將深入探討建造者模式的細節,包括其提供的靈活性和應用場景。

通過對這些模式的比較與對比,我將討論它們在不同情境下的優缺點,並提供最佳實踐指南,以幫助開發人員更好地理解何時以及如何應用這些創建模式來改進代碼的可讀性、可維護性和擴展性


Factory vs Builder 模式

假設兩個模式都會創建同樣的物件,那他們的差異在於

● Factory 模式:關注於整體創建物件的方法

● Builder 模式:關注物件建構的過程

Factory 工廠整體

● 再次強調,站在使用者角度來說,他們 使用 Factory 代表了,他們不關注於產品生產的細節,而關注於產品的整體

● 以下實現一個簡單(靜態)工廠

A. IProducr 抽象產品:定義各個產品的共通點,好讓工廠類可以使用;工廠類則關注這個界面(契約)


interface IChair {

    fun getChairPrice() : Int

}

B. ConcreteProduct 類:具體產品,工廠並不關注各個產品的細節


class PlasticChair : IChair {

    override fun getChairPrice(): Int {
        return 300
    }

}

class WoodenChair : IChair {

    override fun getChairPrice(): Int {
        return 1000
    }

}

C. Factory 類:對外面對使用者的工廠類,其內部可以包裝不同細節,對外使用者則不關心這些細節,只要創建出相同產品即可;這裡我們簡單創建個類而已


class ChairFactory private constructor() {

    enum class ChairType {
        PLASTIC,
        WOODEN,
    }

    companion object {
        fun getChair(type: ChairType) : IChair {
            return when(type) {
                ChairType.PLASTIC -> PlasticChair()
                ChairType.WOODEN -> WoodenChair()
            }
        }
    }

}

Builder 建構者細節

Builder 相對來說,更關注於細節,透過不同方式的組成(甚至可能包括組成順序)都有可能創建不同特性的產品

● 這裡我們讓 Builder 與 Factory 產品類創建相同產品

A. Produce 類:這裡的產品我們取用最簡單的實體類來看,這個產品是一個不變的產品


data class Chair(val price: Int, val material: ChairType) {
    enum class ChairType {
        PLASTIC,
        WOODEN,
    }
}

B. Builder 抽象類:Builder 是對於產品類的描述與包裝,在這個案例中 它不會改變最終的產品實體,但是它可以對產品有不同的描述


abstract class BuilderChair {

    // 對於產品的描述
    var price = 500

    var material: Chair.ChairType = Chair.ChairType.PLASTIC

    abstract fun build() : Chair

}

C. ConcreteBuilder 類:透過抽象 Builder 來對於產品的描述,接著 實體類就可以依照不同特性去創建產品


class PlasticChair2 : BuilderChair() {
    override fun build(): Chair {
        price = 300
        material = Chair.ChairType.PLASTIC
        return Chair(price, material)
    }

}

class WoodenChair2 :  BuilderChair() {

    override fun build(): Chair {
        price = 1000
        material = Chair.ChairType.WOODEN
        return Chair(price, material)
    }

}

D. Director 類:對外使用者接觸的類,它跟 Factory 比較起來提供了更多個方法(契約),這些都代表了個別不同的產品


class Director {

    fun getWoodenChair() : BuilderChair {
        return WoodenChair2()
    }


    fun getPlasticChair() : BuilderChair {
        return PlasticChair2()
    }

}

Factory vs. Builder 最佳實踐

● 兩者都是用來創建物件,但我們可以關注以下兩點,就可以發現明顯的不同

A. 意圖不同

Factory 關注產品的整體,不關心產品如何產生(順序、組裝難度、使用部件... 等等都不關心),它是一個比較粗線條的創建

● 相對於 Factory,Builder 模式更加關注於產品的細節,使用者會專注產品的組裝、選件、順序... 等等;透過細節一步一步的描繪出最終的產品

B. 複雜度不同

● 既然 Factory 關注於整體,那使用者只須關注於 Factory 的契約(interface)即可,不必關注更多,相對來講 Factory 的內聚程度高,使用起來簡單不複雜

單一性,缺點顯而易見,不易調整

● 透過 Builder 創建出來的物件,都擁有一定程度上的獨特性,使用者必須關注較多的細節(或是說更多個契約),才能製造出他們想要的產品

缺點跟 Factory 相反,內聚程度稍低,但可調性高

● 要取用哪個呢?

這要取決於系統(需求)設計時的意圖,依照需求去判定、推斷要使用哪種模式


Abstract Factory vs Builder

Abstract Factory 模式:對於使用者來說,抽象工廠隱藏細節,關注工廠產生的 Product;對於抽象工廠內部來說,透過不同的 Factory 會使用不同的 Product

更關注產品的整體如何組合 Factory 需要的功能

重點在於:抽象工廠有 一系列生產 Product 的方法,透過不同維度的產品組合去支援工廠

Builder 模式:建造者同上所述,它關注於產品生產的細節(順序、參數... 等等)來建構出客製化的產品

Abstract Factory:一系列抽象產品

Abstract Factory 的重點在於,該工廠產生一系列有相關該界面的抽象產品(返回一個抽象界面給使用者操作);以下是抽象工廠案例

A. Abstract Factory 界面:該類聚集一系列的抽象,是抽象工廠的核心抽象(抽象匯聚的邊界由業務需求而定),返回的產品也皆是抽象產品


interface IHomeAppliancesFactory {
    fun getAirPurifier() : IAirPurifier

    fun getTatungAirPurifier() : IDehumidifier
}

B. Abstract Product 界面:這些產品界面是對於外部使用產品者的契約界面,不會提供細節


interface IAirPurifier {
    fun rotatable() : Boolean
}

// ----------------------------------------------------

interface IDehumidifier {
    fun silentMode() : Int
}

C. Concrete Product 類:實做產品,藉由它可以實現每個產品的特色細節,但使用者不必知道,它僅需要完成對於抽象界面的契約即可


class LGAirPurifier : IAirPurifier {
    override fun rotatable(): Boolean {
        return true
    }
}

class TatungAirPurifier : IAirPurifier {
    override fun rotatable(): Boolean {
        return false
    }
}

// ----------------------------------------------------

class LGDehumidifier : IDehumidifier {
    override fun silentMode(): Int {
        return 5
    }
}

class TatungDehumidifier : IDehumidifier {
    override fun silentMode(): Int {
        return 3
    }
}

D. Concrete Factory 類:該類會使用實際的產品,所以它需要知道實際產品的特色(但它仍是返回一個抽象,所以歸類唯一賴抽象)


class LGHomeAppliancesFactory : IHomeAppliancesFactory {
    override fun getAirPurifier(): IAirPurifier {
        return LGAirPurifier()
    }

    override fun getPhilipsAirPurifier(): IDehumidifier {
        return LGDehumidifier()
    }

}

// ----------------------------------------------------

class TatungHomeAppliancesFactory : IHomeAppliancesFactory {
    override fun getAirPurifier(): IAirPurifier {
        return TatungAirPurifier()
    }

    override fun getTatungAirPurifier(): IDehumidifier {
        return TatungDehumidifier()
    }

}

● 抽象工廠對外隱藏了實做的細節(如同工廠模式),使用只須關注每個工廠界面方法的特色(對外的契約承諾)即可,並且 製造出來的產品注重整體性、不可修改產品

Builder 建構者的自由度、細節

● 相較起來 Builder 也可以作到相同的事情,不過 它可以自由在 Builder 設計之內(Director 類),依照藍圖輕鬆調整細節(產品在最終完成前可修改)

A. Builder 抽象類:定義了產品藍圖(產品特性、最終生產類),但不定義細節


abstract class HomeAppliancesBuilder {

    // 產品特性
    var rotatable = false
    var silentMode = 0

    // 最終生產類
    abstract fun createAirPurifier() : IAirPurifier

    abstract fun createDehumidifier() : IDehumidifier
}

這裡的 Builder 抽象類,相對於 Abstract Factory 的抽象類

B. ConcreteBuilder 類:產品的細節,負責決定最終製造出來的產品類 (這裡使用簡單的匿名類);可以在這裡定義一些產品預設參數


class LGAppliancesBuilder : HomeAppliancesBuilder() {
    init {
        rotatable = true
        silentMode = 3
    }

    override fun createAirPurifier(): IAirPurifier {
        return object : IAirPurifier {
            override fun rotatable(): Boolean {
                return rotatable
            }
        }
    }

    override fun createDehumidifier(): IDehumidifier {
        return object : IDehumidifier {
            override fun silentMode(): Int {
                return silentMode
            }
        }
    }
}

class TatungAppliancesBuilder : HomeAppliancesBuilder() {
    init {
        rotatable = false
        silentMode = 5
    }

    override fun createAirPurifier(): IAirPurifier {
        return object : IAirPurifier {
            override fun rotatable(): Boolean {
                return rotatable
            }
        }
    }

    override fun createDehumidifier(): IDehumidifier {
        return object : IDehumidifier {
            override fun silentMode(): Int {
                return silentMode
            }
        }
    }
}

C. Direct 類:它是面對使用者的類,由它來決定產品的變化

● 從這個類中可以發現,它比起 Abstract Factory 可以更自由的調整產品的細節,決定生產更多的產品(輕鬆變化)


class BrandDirector {

    private val lg = LGAppliancesBuilder()
    private val tatung = TatungAppliancesBuilder()

    fun flagshipLGAirPurifier() : IAirPurifier {
        return lg.createAirPurifier()
    }

    fun basicLGAirPurifier() : IAirPurifier {
        return lg.apply {
            rotatable = false
        }.createAirPurifier()
    }

    fun flagshipLGDehumidifier() : IDehumidifier {
        return lg.createDehumidifier()
    }

    fun basicLGDehumidifier() : IDehumidifier {
        return lg.apply {
            silentMode = 3
        }.createDehumidifier()
    }

    fun flagshipTatungAirPurifier() : IAirPurifier {
        return tatung.createAirPurifier()
    }

    fun basicTatungAirPurifier() : IAirPurifier {
        return tatung.apply {
            rotatable = false
        }.createAirPurifier()
    }

    fun flagshipTatungDehumidifier() : IDehumidifier {
        return tatung.createDehumidifier()
    }

    fun basicDatungDehumidifier() : IDehumidifier {
        return tatung.apply {
            silentMode = 3
        }.createDehumidifier()
    }

}

Abstract Factory vs. Builder 最佳實踐

● 兩者都是用來創建多個一系列相關物件,不過他們有幾點不同

A. 對於產品的看法不同

Abstract Factory 知道產品特色,但是不關心產品細節,也很少去操作產品細節

是用更高層次的角度來觀看產品

Builder 知道品哪有哪些細節(並且在需要時也關注),也可以操作產品細節

使用較細節角度,去關注產品

B. 對於產品的拓展

Abstract Factory 對於產品關住在整體,修改較費力,必須一系列產品接修改,但它十分適合用在一個固定幾類不常變動的產品

Builder 與之想反,產品細節的操控可以讓它快速生產不同特色的產品,但缺點在於對於細節的操控,有點不符合開閉原則


更多的物件導向設計

物件導向的設計基礎如下,如果是初學者或是不熟悉的各位,建議可以從這些基礎開始認識,打好基底才能走個更穩(在學習的時候也需要不斷回頭看)!

設計建模 2 大概念- UML 分類、使用

物件導向設計原則 – 6 大原則(一)

物件導向設計原則 – 6 大原則(二)

創建、行為、結構型設計 8 個比較 | 包裝模式 | 最佳實踐

創建模式:Creation Patterns

創建模式 PK

創建模式 - Creation Patterns

結構模式:Structural Patterns

結構模式 PK

結構模式 - Structural Patterns

結構模式專注於「物件之間的組成」,以形成更大的結構。這些模式可以幫助你確保當系統進行擴展或修改時,不會破壞其整體結構。例如,外觀模式、代理模式… 等等,詳細解說請點擊以下連結

Bridge 橋接模式 | 解說實現 | 物件導向設計

Decorate 裝飾模式 | 解說實現 | 物件導向設計

Proxy 代理模式 | 解說實現 | 分析動態代理

Iterator 迭代設計 | 解說實現 | 物件導向設計

Facade 外觀、門面模式 | 解說實現 | 物件導向設計

Adapter 設計模式 | 解說實現 | 物件導向設計

Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

發表迴響