適配器模式 adapter 結構型 設計模式(九)

現實世界中的適配器模型

 
先來看下來幾個圖片,截圖自淘寶
image_5b907301_7af1
上圖爲港版的插頭與港版的插座
 
image_5b907302_77b6
上圖爲插座適配器賣家的描述圖
 
image_5b907302_3d4c
上圖爲適配後的結果

現實世界中適配器模式 角色分類

這就是適配器模式在電源插座上的應用
咱們看下在插座適配器中的幾個重要角色
image_5b907303_2709
 
能夠看得出來,大陸和港版插座面板,都是做爲電源的角色,他們的功能是類似的或者說相近的
插頭要使用插座,進而接通電流

現實世界到代碼的轉換 電源插座代碼示例

港版插座面板

package adapter;
/**目標角色 Target 接口
* 香港地區使用的插座面板,提供輸出電流的功能
* @author noteless
*
*/
public interface TargetHongkongPanelInterface {
public void offerHongKongElectricity();
}

package adapter;
/**目標角色 Target 某個具體的港版插座面板 實現類
* 香港地區使用的插座面板,提供輸出電流的功能
* @author noteless
*
*/
public class TargetHongkongPanel implements TargetHongkongPanelInterface{
@Override
public void offerHongKongElectricity() {
System.out.println("港版面板 提供電流");
}
}

大陸地區插座面板java

package adapter;
/**被適配角色 Adaptee 接口
* 大陸地區使用的插座面板,提供輸出電流的功能
* @author noteless
*
*/
public interface AdapteeChinaMainlandPanelInterface {
public void offerChinaMainlandElectricity();
}

package adapter;
/**被適配角色 Adaptee 某種具體類型的插座面板 實現類
* 大陸地區使用的插座面板,提供輸出電流的功能
* @author noteless
*/

public class AdapteeChinaMainlandPanel implements AdapteeChinaMainlandPanelInterface{
@Override
public void offerChinaMainlandElectricity() {
System.out.println("國標面板 提供電流");

}
}

港版插頭

package adapter;
/**客戶角色 Client 港版插頭
* @author noteless
*
*/
public class ClientHongKongSocket {
/**接受港版插座面板做爲參數
* 港版插頭,插入到港版插座面板
* @param targetHongkongPanel
*/
public void plugIn(TargetHongkongPanelInterface targetHongkongPanel) {
targetHongkongPanel.offerHongKongElectricity();
}
/*
* 測試主程序,港版插頭 插入到適配器上
* 適配器插入到大陸面板上
*/
public static void main(String ...args) {
//港版插頭
ClientHongKongSocket socket = new ClientHongKongSocket();
//大陸面板
AdapteeChinaMainlandPanel adapteeChinaMainlandPanel = new AdapteeChinaMainlandPanel();
//適配器
Adapter adapter = new Adapter(adapteeChinaMainlandPanel);
//港版插頭 插到 適配器上
socket.plugIn(adapter);
}
}

插頭適配器

package adapter;
/**適配器角色 Adapter
* 實現目標角色 TargetHongkongPanelInterface
* 組合使用被適配角色 AdapteeChinaMainlandPanelInterface
* 將對目標角色的方法調用轉換爲被適配角色的方法調用
* @author noteless
*
*/
public class Adapter implements TargetHongkongPanelInterface{
private AdapteeChinaMainlandPanelInterface adapteeChinaMainlandPanel;

Adapter(AdapteeChinaMainlandPanel adapteeChinaMainlandPanel){
this.adapteeChinaMainlandPanel = adapteeChinaMainlandPanel;
}

@Override
public void offerHongKongElectricity() {
adapteeChinaMainlandPanel.offerChinaMainlandElectricity();
}
}

執行港版插頭的測試main方法

image_5b907303_2fc7
 

UML圖

港版插頭ClientHongKongSocket與港版插座面板 TargetHongKongPanelInterface接口關聯
Adapter 實現了  港版插座面板 TargetHongKongPanelInterface接口
而且包含一個      大陸插座面板  AdapteeChinaMainlandPanelInterface 接口
適配器將對 港版插座面板的方法調用  轉換爲  大陸插座面板的方法調用
這就是整個適配器結構( 能夠不關注實現類)
image_5b907303_2360
 
image_5b907303_4009
 
客戶角色Client 要使用 目標角色Target
適配器模式就是要 冒充 目標角色Target,看起來有目標角色的行爲
在OOP中,想要作到 就是實現或者繼承或者擁有一個成員
總之:
適配器就是把被適配者轉換爲爲目標

OOP中的適配器模式詳解

意圖:
將一個類的接口轉換成客戶但願的另一個接口。
適配器模式使得本來因爲接口不兼容而不能一塊兒工做的那些類能夠一塊兒工做.
注意:此處說的接口,並非單純的指Interface,而是指一切能夠提供方法調用的類型,多是接口也多是類
客戶使用適配器的過程:
客戶經過目標接口調用適配器的方法,對適配器發出請求
適配器使用被適配者接口把請求進行處理
客戶接收到調用的結果,可是並未察覺這一切是適配器在起轉換做用.
 

適配器分類

適配器三種模式
類適配器   
對象適配器
接口適配器
想要把一個類的接口轉換爲客戶但願的另一個接口
必需要有輸入輸出,有目標  有源
因此做爲一個適配器,必需要 一手拿着被適配者也就是源  另外一手拿着的是目標
想要轉變爲目標,那麼必須得同目標時同樣的類型,   在oop中想要成爲目標類型 要麼繼承 要麼實現
想要擁有被適配者,要麼繼承,要麼實現,要麼就是關聯(擁有一個對象)
三種方式能夠理解爲按照  擁有被適配者 的方式進行劃分的
若是繼承Adaptee,那麼就是類 適配器
若是擁有一個Adaptee,也就是擁有一個Adaptee對象,那麼就是對象 適配器
若是實現Adaptee,那麼就是 接口適配器
 
如今回想下,咱們上面的例子
適配器 實現了目標接口,而且擁有一個Adaptee對象 做爲屬性,很顯然就是對象適配器
 
類適配器
根據上面的描述,若是繼承Adaptee,那麼就是類 適配器,
在Java中不容許多繼承,既然已經繼承了Adaptee  ,那麼就必需要求目標是一個接口(此處接口就是Interface)
這就有必定的侷限性
並且,既然是繼承被適配者類,那麼,被適配者的子類擁有的方法和行爲,他並不能擁有,也就是說不能適配被適配者的子類
優勢,那就是,適配器做爲被適配者的子類,天然擁有更多的操做空間,好比重寫方法
 
 
對象適配器
如同咱們上面的例子同樣,若是把被適配者當作一個屬性對象放到適配器中,這就是對象適配器
顯然,他不要求目標必定是接口, 繼承仍是實現均可以
同類適配器比較的話,顯然,他不能對被適配者 原來的一些方法進行操做,只能進行使用,不過也無傷大雅,不算缺點
由於他是擁有一個被適配者類型的對象,那麼,被適配者和他的子類顯然均可以做爲具體的對象傳入
 
 
接口適配器
按照咱們的描述,若是實現了被適配者 Adaptee  那麼就是接口適配器
具體說來:
不須要所有實現接口提供的方法
可先設計一個抽象類實現接口,併爲該接口中每一個方法提供一個默認實現(空方法)
那麼該抽象類的子類可有選擇地覆蓋父類的某些方法來實現需求
適用於一個接口不想使用其全部的方法的狀況
 

接口適配器示例

接口

package interfaceadapter;
public interface Interfaces {
public void method1();
public void method2();
public void method3();
public void method4();
public void method5();
}

抽象類

package interfaceadapter;
/**
* @author noteless
*
*/
public abstract class AbstractClass implements Interfaces {
@Override
public void method1() { }
@Override
public void method2() { }
@Override
public void method3() { }
@Override
public void method4() { }
@Override
public void method5() { }
}

兩個實現類

package interfaceadapter;
public class ImplementClass1 extends AbstractClass {
@Override
public void method1() {
System.out.println("method1 called ");
}
@Override
public void method3() {
System.out.println("method3 called ");
}
@Override
public void method5() {
System.out.println("method5 called ");
}
}

package interfaceadapter;
public class ImplementClass2 extends AbstractClass {
@Override
public void method2() {
System.out.println("method2 called");
}

@Override
public void method4() {
System.out.println("method4 called");
}
}

測試類-客戶角色

package interfaceadapter;
public class Test {
public static void main(String[] args) {
Interfaces class1 = new ImplementClass1();
Interfaces class2 = new ImplementClass2();
class1.method1();
class1.method2();
class1.method3();
class1.method4();
class1.method5();
System.out.println("------");
class2.method1();
class2.method2();
class2.method3();
class2.method4();
class2.method5();
}
}

 

image_5b907303_60dd
 
接口適配器的行爲至關於適配了本身
把原來的接口 當作被適配者
目標則是一個實現了接口部分功能的類
調用這個接口的部分方法場景下,上面的形式是很是方便的
 
從這個示例中或許應該更加能理解適配器的本意:
將一個類的接口轉換成客戶但願的另一個接口。
適配器模式使得本來因爲接口不兼容而不能一塊兒工做的那些類能夠一塊兒工做
因此說,但凡是涉及到轉換這一律念,你均可以考慮這個思惟模式
三種經常使用形式,只是概念的表現形式而已
並且,實際的問題場景將會有不少種,也不可能徹底都按照某種格式
 
 
再好比雙向適配器
便可以將被適配者轉換爲目標
也能夠把目標轉換爲被適配者

雙向適配器

目標接口/目標實現類

package doubleadapter;
public interface TargetInterface {
void targetRequest();
}



package doubleadapter;
public class TargetImplClass implements TargetInterface{
@Override
public void targetRequest() {
System.out.println("targetRequest ... ");
}
}

被適配者接口/被適配者實現類

package doubleadapter;
public interface AdapteeInterface {
void adapteeRequest();
}

package doubleadapter;
public class AdapteeImplClass implements AdapteeInterface{
@Override
public void adapteeRequest() {
System.out.println("adapteeRequest ... ");
}
}

適配器

package doubleadapter;
public class Adapter implements TargetInterface,AdapteeInterface {
private TargetInterface target;
private AdapteeInterface adaptee;
Adapter(TargetInterface target){
this.target = target;
}

Adapter(AdapteeInterface adaptee){
this.adaptee = adaptee;
}
@Override
public void adapteeRequest() {
target.targetRequest();
}
@Override
public void targetRequest() {
adaptee.adapteeRequest();
}
}

Client 客戶端角色

Main方法就至關於Client 客戶端角色
image_5b907303_4265
 
適配器Adapter模式的宗旨是:
保留現有類所提供的服務,向客戶提供接口,以知足客戶的指望,也就是將現有接口轉換爲客戶但願的另外的一個接口
本質在於轉換
 

JDK中的小應用

image_5b907303_1877
image_5b907303_1230
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.apache.commons.collections.iterators;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
public class EnumerationIterator implements Iterator { private Collection collection; private Enumeration enumeration; private Object last; public EnumerationIterator() { this((Enumeration)null, (Collection)null); } public EnumerationIterator(Enumeration enumeration) { this(enumeration, (Collection)null); } public EnumerationIterator(Enumeration enumeration, Collection collection) { this.enumeration = enumeration; this.collection = collection; this.last = null; } public boolean hasNext() { return this.enumeration.hasMoreElements(); } public Object next() { this.last = this.enumeration.nextElement(); return this.last; } public void remove() { if (this.collection != null) { if (this.last != null) { this.collection.remove(this.last); } else { throw new IllegalStateException("next() must have been called for remove() to function"); } } else { throw new UnsupportedOperationException("No Collection associated with this Iterator"); } } public Enumeration getEnumeration() { return this.enumeration; } public void setEnumeration(Enumeration enumeration) { this.enumeration = enumeration; } }

 

Enumeration 和 Iterator 你們應該都聽過
Enumeration算是遺留的老代碼了
很顯然,咱們但願可以使用新世界的Iterator
怎麼辦呢?
答案就是適配器
目標是 Iterator   被適配者是 Enumeration
看代碼可知:
public class EnumerationIterator implements Iterator {
    private Enumeration enumeration;
 
他實現了Iterator  而且有一個Enumeration 的成員,是  對象適配器
nextElement()  與  next()
hasMoreElements  與  hasNext()    
他們能夠說是匹配的
可是Enumeration 是不能刪除的,沒辦法搞remove方法
因此說源碼中提供了能夠傳入集合的構造方法,把對應的集合也傳入進去
而且設置last變量記住剛纔的位置
若是傳遞了集合 而且last存在,那麼能夠執行remove
不然拋異常
 
 
設計模式是做爲解決問題或者設計類層級結構時的一種思惟的存在,而不是公式同樣的存在!
相關文章
相關標籤/搜索