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

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

Overview of Content

如有引用參考請詳註出處,感謝

本文將探討 Adapter 設計模式,著重於實現和物件導向設計的相關解說。首先,我們將介紹 Adapter 的定義,明確解釋其在物件導向編程中的角色和功能。接著,探討 Adapter 設計的使用場景,以及在實際應用中如何運作。

進一步深入,我們將提供 Adapter 的 UML 建模圖,以視覺化方式呈現其結構和關係。同時,本文將探討 Adapter 設計的優缺點,幫助讀者了解在何種情境下選擇使用這種設計模式。

在「Adapter 實現」的部分,我們將進行更詳細的探討,包括 Adapter 類別繼承Adapter 物件聚合 的實際實現方式。透過具體的程式碼示例和說明,讀者將更容易理解 Adapter 設計模式的應用和實現細節。

總體而言,本文將提供全面的介紹,從 Adapter 的基本概念到具體實現,以協助讀者深入理解並應用 Adapter 設計模式於其物件導向設計中


Adapter 定義

將原本 A 類的接口轉為另外一個 B 類可用的接口,使兩個原本不同的接口可以批配使用

在做程式設計時,其實並不會一開始就將 Adapter 考慮進去,因為 Adapter 主要是運作在兩個已經正常運行的接口(界面)上

Adapter 概念其實也來自於建築設計,它的概念圖如下,在接合兩個不同的物件界面


Adapter 設計使用場景

無法兼容:系統需要此現有的類,但是目前類別的介面不符合要求

共用介面:需要建立一個共用介面,提供重複使用,用於彼此之間沒有特別關係的類別,包括以後想要引進的類別

不可預知性:一個統一輸出的介面,但輸入端類型多種,也就存在多種可能性,這時就可以使用適配器

Adapter 的重點是在「偽裝,它與原本的類 (或其家族) 沒有關係,只是很相像

Adapter 可以將兩個不相容的類融合在一起(類似黏著劑),讓它們能夠協做,最終達到相同目的,使兩個不匹配的物件能夠共同工作

Adapter UML 建模圖

● Adapter 角色介紹

角色功能、說明
Target原本就運行的一個類,使用者就使用這個 Target(舊的界面 或 類)
Original原本就運行的一個類,是原本自身設計好的 Original 接口 (也就是新的界面)
AdapterAdapter 的核心角色,把 Target 轉換成 Original 讓我們自己使用,這轉會 可以通過繼承 or 關聯去做

● Adapter 有兩種實現方式

A. 類別 Adapter (繼承、實現)

使用繼承好嗎 ?

使用繼承原物件的方式,其代價較高,如同里式原則的缺點,可能會繼承到不必要使用的方法,導致使用者混淆,越多類別會導致繼承關係越重

B. 物件 Adapter (聚合、關聯):如果你需要接合多個不同接口時就可以使用聚合或關聯關係

Adapter 設計:優缺點

Adapter 設計優點

更好的重用性、擴充性: 方便重用舊系統的開發 & 新系統的開發

Adapter 設計缺點

過多 Adapter 會導致系統的混亂,不易整體掌握 (無法掌握原始類別真正得實現),可能常會發生呼叫類別錯誤,所以如果不是必要盡量不要使用 (或不要創建太多 Adapter)


Adapter 實現

Adapter 類別繼承

A. Original 類:原本就存在你自己設計的電壓

interface Volt {
    fun get110Volt() : Int

}

open class Volt110 : Volt {

    override fun get110Volt(): Int {
        return 110
    }

}

B. Target 目標類:原本以存在的外部類,而使用者正在使用這個類,我們的目標就是轉換 Volt 接口到 ExternalVolt

interface ExternalVolt {

    fun get220Volt() : Int

}

open class Volt220 : ExternalVolt {
    override fun get220Volt(): Int {
        return 220
    }

}

C. Adapter繼承 Target(Volt220) 類、Original(ExternalVolt) 接口(使用者才能替換)

class VoltAdapter : Volt220(), Volt {

    override fun get110Volt(): Int {
        return get220Volt()
    }

}

使用範例:由於對外 使用者是使用 Volt 界面,所以完全不須改變,直接將其設定成 Adapter 類,就可以進行轉換

fun main() {

    var volt : Volt = Volt110()

    println("Before adapter: ${volt.getMyVolt()}")

    volt = VoltAdapter()

    println("After adapter: ${volt.getMyVolt()}")

}

Adapter 物件聚合

● 將繼承替換成 setter 或建構函數注入,這樣就不會有依賴實體類繼承的困擾,可以透過注入抽象來達到不同的結果

A. Adapter:透過建構函數注入


class VoltAdapter2 constructor(private val volt: ExternalVolt): Volt {

    override fun get110Volt(): Int {
        return volt.get220Volt()
    }

}

● 使用範例:

使用方式差不多,不過這邊使用者要 自己選擇 Adapter 類


fun main() {
    
    var volt : Volt = Volt110()

    println("Before adapter: ${volt.getMyVolt()}")

    // Adapter
    volt = VoltAdapter2(Volt220())

    println("After adapter: ${volt.getMyVolt()}")

}

● 但這裡我們也可以很清楚的看到,使用者會需要指定注入類(暴露了底層實做,不符合迪米特原則(最少知識原則)


更多的物件導向設計

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

設計建模 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?

發表迴響