Try   HackMD

Design Pattern - Adapter

前言

你是否曾經遇過辛辛苦苦開發了一個不錯的程式包,卻因為一點點的差異導致無法被重複應用。

這個差異可能是 5%、10%、20% 左右,但卻造成了整包東西無法被再次使用、整合的情境。

今天在專案中引用一個 A 服務,隔天被告知要更換成 B 服務,幾周後又要改成 C 服務,而來自 A B C 的服務本身或多或少都有差異,造成每一次的改變都必須更動到原本的程式邏輯才能完美契合。

但對於程式碼的維護來說卻是糟糕的處理方式,每一次的更正都必須要跑一次測試。

這就像過去有許許多多不同規格的電線插頭一樣,為了不同的插頭而去更換機械部件的插座,非常不符合效益。

今天要說到的 Adapter 正是後來出現的 "轉接頭",他實現了不同規格仍能對接的期望,避免需要更換插座或是更換插頭的狀況。

主題

Adapter Pattern

中文常見名稱 建造者模式

屬於 Structural Design Pattern 一種常見的設計理念。

目的是幫助工程師在對接服務的時候,避免需要動到對街服務本身的邏輯,從而避免了可能因改動造成的問題。確保了程式本身的獨立與完整。

優點:

  • 底層邏輯與實務邏輯(Adapter層)職責分離,增加底層邏輯的重複使用性
  • 符合 S.O.L.I.D 原則
  • 高擴展性

缺點:

  • 過度的使用 Adapter Pattern 會增加整體架構的複雜性與可維護性
  • 較直接使用底層邏輯來的低效
  • 測試的難度較高,錯誤的來源有更多的可能性

Adapter Design Pattern 又分為兩種:

  • Object Adapter
  • Class Adapter

Object Adapter

透過實作指定 Interface 的 Adapter 類,並放入想要轉換的類方式實現調配

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

圖片來源於 Adapter Design Pattern Wiki

Class Adapter

透過多重繼承的方式進行調配,適用於 C++、Python,Java 因為不支援多重繼承,故需要改寫成繼承與實作並行的方式實現調配

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

圖片來源於 Adapter Design Pattern Wiki

實行方式

public class Adapter { public static void main(String [] args){ InitService1 service1 = new Service1Impl(); InitService2 service2 = new Service2Impl(); service1.service_1(); service1.service_2(); service2.service_3(); service2.service_4(); AdapterService1ToService2 adapterService = new AdapterService1ToService2(service1); adapterService.service_3(); adapterService.service_4(); } } interface InitService1{ void service_1(); void service_2(); } class Service1Impl implements InitService1{ @Override public void service_1() { System.out.println("This is Service 1 Service_1()."); } @Override public void service_2() { System.out.println("This is Service 1 Service_2()."); } } interface InitService2{ void service_3(); void service_4(); } class Service2Impl implements InitService2 { @Override public void service_3() { System.out.println("This is Service 2 Service_3()."); } @Override public void service_4() { System.out.println("This is Service 2 Service_4()."); } } class AdapterService1ToService2 implements InitService2 { private InitService1 service1Impl; public AdapterService1ToService2(InitService1 initService1){ this.service1Impl = initService1; } @Override public void service_3(){ /* code ... */ System.out.println("This is Adapter."); service1Impl.service_1(); /* code ... */ } @Override public void service_4(){ /* code ... */ System.out.println("This is Adapter."); service1Impl.service_2(); /* code ... */ } }
Execute Results:

This is Service 1 Service_1().
This is Service 1 Service_2().
This is Service 2 Service_3().
This is Service 2 Service_4().
This is Adapter.
This is Service 1 Service_1().
This is Adapter.
This is Service 1 Service_2().

範例中有 InitService1InitService2 兩介面,與實作介面的實體 Service1ImplService2Impl

Kai 這邊建立一個將 Service1 轉為 Service2 的 Adapter 類別 AdapterService1ToService2,由於目標是轉成 Service2,故需實作 InitService2

除了實作介面的方法外,還包含將 Service1 放入的方法,在使用上就可以透過放入 Service1 的方式對接 Service2 的服務了

用一句話介紹 Adapter Pattern: 一個轉接頭概念的設計模式

首頁 Kai 個人技術 Hackmd

tags: Design Pattern