iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 15
0

本文同步分享於個人blog

  • 定義


The Adapter Pattern converts the interface of a class into another interface the clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.

翻譯年糕:適配器模式將一個類別的介面轉換成客戶希望的另外一個介面,Adapter使得原本由於介面相容而不能一起工作的那些類別可以一起工作

最常見的例子就是電器插頭與插座。當出國玩時,一定會有使用電器的需求。但每個國家的插座及電壓都不一樣,那該怎麼辦?這時你就會拿出你事先準備好的轉接頭,藉由轉接頭將插座與電器插頭做連接。生活上還有許多的例子,如type-c轉hdmi線...等等。

AdapterPattern

註:圖片取自這

  • Adapter Pattern 成員

又到了介紹家族成員的時候了。Adapter Pattern內有三個成員

成員 功用
Target 定義著所需要的方法,可以是抽象類別或是介面。如:國外的插座。(白話文:想要變成的對象)
Adaptee 為呼叫和配接的元件中的组件接口。如:身上的電器插頭。(白話文:想變身的對象)
Adapter 為一個轉換器,通過繼承或是合成的方式,將Adaptee的接口轉換成Adapter接口,Client直接呼叫Adapter接口就可以執行Adaptee的方法。如:轉接頭。(白話文:把想變身的對象變成他想變成的對象,這邊稱他上帝XD)
  • Object Adapter & Class Adapter


Adapter Pattern內有分兩種模式,一種是物件結構型模式,另一種是類別結構型模式。類別結構型模式的耦合度會比物件模式要高,且類別結構型模式使用多繼承,所以不能使用在JAVA中。

物件適配器(Object Adapter):

在這種模式中,用合成的方式,將Adaptee放入Adapter中。

註:什麼是合成,可參考這篇:汽油車 & 電動車 - 合成/聚合複用原則 | Composite/Aggregate Reuse Principle內的合成 v.s 聚合

Object_Adapter_wiki1

Object_Adapter_wiki2

註:圖片取自wiki

interface Target {
    public void request();
}
class Adaptee {
    public void specificRequest() {       
        System.out.println("run specificRequest in Adaptee class");
    }
}
class ObjectAdapter implements Target {
    private Adaptee adaptee;
    public ObjectAdapter(Adaptee adaptee)
    {
        this.adaptee=adaptee;
    }
    public void request()
    {
        adaptee.specificRequest();
    }
}
public class ObjectAdapterTest {
    public static void main(String[] args)
    {
        System.out.println("ObjectAdapterTest:");
        Adaptee adaptee = new Adaptee();
        Target target = new ObjectAdapter(adaptee);
        target.request();
    }
}

output

ObjectAdapterTest:
run specificRequest in Adaptee class

類別適配器(Class Adapter):

透過多重繼承來實現,adapter同時繼承兩個以上的類別。這種方式在支援多繼承的語言中出現,如:C++、python。而Java中無法實作多重繼承,但可以定義一個Adapter來實作Target,同時繼承Adaptee的方式來實現Class Adapter。

Class_Adapter_wiki1

Class_Adapter_wiki2

註:圖片取自wiki

python範例:

class Adaptee1:
    def doAction1(self):
        print("action 1")

class Adaptee2:
    def doAction2(self):
        print("action 2")

class Adaptor(Adaptee1, Adaptee2):
    def doRequest(self):
        self.doAction1()
        self.doAction2()
        print("request")

adaptor = Adaptor()
adaptor.doRequest()

output

action 1
action 2
request   

註:可將程式碼在 online_python_compiler 執行

Java範例:

interface Target {
    public void request();
}
class Adaptee {
    public void specificRequest()
    {       
        System.out.println("run specificRequest in Adaptee class");
    }
}
class ClassAdapter extends Adaptee implements Target {
    public void request()
    {
        specificRequest();
    }
}
public class ClassAdapterTest {
    public static void main(String[] args)
    {
        System.out.println("ClassAdapterTest:");
        Target target = new ClassAdapter();
        target.request();
    }
}

output

ClassAdapterTest:
run specificRequest in Adaptee class

雖然Java這樣依舊可以執行,但如果要有多個Adaptee,Class Adapter就不在適用了。

  • Adapter Pattern 實作


看完Adapter Pattern的成員以及兩種Adapter之後,就來實作看看Adapter Pattern吧!

來用小紅帽與大野狼的故事來寫看看Adapter Pattern。大野狼為了把小紅帽吃了,所以到老奶奶家,先把奶奶吃了,再假扮成奶奶。我們就不實作吃奶奶的步驟了,直接從假扮奶奶開始。

分別將奶奶與大野狼的Target做出來,然後再分別實作他們。

interface Grandma {
    public void snore();
    public void sleep();
}
interface Wolf {
    public void bark();
    public void sleep();
}
class RedHatGrandma implements Grandma {
    @Override
    public void snore() {
        System.out.println("Grandma snore");
    }
    @Override
    public void sleep() {
        System.out.println("Grandma sleeping");
    }
}
class BigWildWolf implements Wolf {
    @Override
    public void bark() {
        System.out.println("BigWildWolf bark");
        System.out.println("BigWildWolf imitate Grandma snore");
    }
    @Override
    public void sleep() {
        System.out.println("BigWildWolf sleeping");
    }
}

接著建立大野狼Adapter,由於大野狼要假扮奶奶,所以他要實作奶奶的Target。

class BigWildWolfAdapter implements Grandma {
    private BigWildWolf wolf;
    public BigWildWolfAdapter(BigWildWolf wolf) {
        this.wolf = wolf;
    }
    @Override
    public void snore() {
        wolf.bark();
    }
    @Override
    public void sleep() {
        wolf.sleep();
    }
}

最後小紅帽就會敲門叫奶奶,這時就會聽到奶奶的打呼聲,跟想要模仿奶奶打呼的狼的叫聲。

class GrandmaHome {
    void call (Grandma grandma){
        grandma.snore();
    }
}
public class RedHat {
    public static void main(String[] args) {
        RedHatGrandma grandma = new RedHatGrandma();
        BigWildWolf wolf = new BigWildWolf();
        Grandma wolfAdapter = new BigWildWolfAdapter(wolf);
        
        GrandmaHome home = new GrandmaHome();
        home.call(grandma);
        home.call(wolfAdapter);
    }

}

output

Grandma snore
BigWildWolf bark
BigWildWolf imitate Grandma snore

在Client中,有奶奶(RedHatGrandma)跟假扮成奶奶的大野狼(BigWildWolfAdapter)。小紅帽執行home.call()的動作,分別傳回奶奶跟大野狼的聲音。可以看到,大野狼成功的假扮成奶奶了!

  • 小結


Adapter Pattern的目標
轉接器模式將一個類別的介面轉換成客戶希望的另外一個介面,Adapter使得原本由於介面相容而不能一起工作的那些類別可以一起工作
Adapter Pattern的成員
Target:想要變成的對象。(國外的插座)
Adaptee:想變身的對象。(繫帶的電器插頭)
Adapter:把想變身的對象變成他想變成的對象,這邊稱他上帝。(繫帶的轉接器)
Adapter Pattern的優缺點
優點
1. 可以讓沒有關聯的類別一起執行。
2. 提高類別的重用性。 
3. 整體靈活度高。
缺點
1. 若使用過多的Adapter會使系統過於複雜凌亂,不易維護。
  • 範例程式碼


範例1:Object Adapter
範例2:Class Adapter(python)
範例3:Class Adapter(java)
範例4:Adapter Pattern

  • References


轉接器模式(Adapter Pattern)
轉接器模式 (Adapter Pattern)
Adapter pattern
Adapter 模式 - Class Adapter


上一篇
[Day14] 原型模式 | Prototype Pattern
下一篇
[Day16] 橋接模式 | Bridge Pattern
系列文
從生活中認識Design Pattern30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言