版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。 https://blog.csdn.net/qingfeng812/article/details/8961265
1. 概述
将一個類的接口轉換成客戶希望的另外一個接口。Adapter模式使得原本由于接口不相容而不能一起工作的那些類可以在一起工作。
2. 解決的問題
即Adapter模式使得原本由于接口不相容而不能一起工作的那些類可以在一起工作。
3. 模式中的角色
3.1 目标接口(Target):客戶所期待的接口。目标可以是具體的或抽象的類,也可以是接口。
3.2 需要适配的類(Adaptee):需要适配的類或适配者類。
3.3 擴充卡(Adapter):通過包裝一個需要适配的對象,把原接口轉換成目标接口。
4. 模式解讀
注:在GoF的設計模式中,對擴充卡模式講了兩種類型,類擴充卡模式和對象擴充卡模式。由于類擴充卡模式通過多重繼承對一個接口與另一個接口進行比對,而C#、java等語言都不支援多重繼承,因而這裡隻是介紹對象擴充卡。
4.1 擴充卡模式的類圖
4.2 擴充卡模式的代碼實作
/// <summary>
/// 定義用戶端期待的接口
/// </summary>
public class Target
{
/// <summary>
/// 使用virtual修飾以便子類可以重寫
/// </summary>
public virtual void Request()
{
Console.WriteLine("This is a common request");
}
}
/// <summary>
/// 定義需要适配的類
/// </summary>
public class Adaptee
{
public void SpecificRequest()
{
Console.WriteLine("This is a special request.");
}
}
/// <summary>
/// 定義擴充卡
/// </summary>
public class Adapter:Target
{
// 建立一個私有的Adeptee對象
private Adaptee adaptee = new Adaptee();
/// <summary>
/// 通過重寫,表面上調用Request()方法,變成了實際調用SpecificRequest()
/// </summary>
public override void Request()
{
adaptee.SpecificRequest();
}
}
4.3 用戶端代碼
class Program
{
static void Main(string[] args)
{
// 對用戶端來說,調用的就是Target的Request()
Target target = new Adapter();
target.Request();
Console.Read();
}
}
運作結果
5. 模式總結
5.1 優點
5.1.1 通過擴充卡,用戶端可以調用同一接口,因而對用戶端來說是透明的。這樣做更簡單、更直接、更緊湊。
5.1.2 複用了現存的類,解決了現存類和複用環境要求不一緻的問題。
5.1.3 将目标類和适配者類解耦,通過引入一個擴充卡類重用現有的适配者類,而無需修改原有代碼。
5.1.4 一個對象擴充卡可以把多個不同的适配者類适配到同一個目标,也就是說,同一個擴充卡可以把适配者類和它的子類都适配到目标接口。
5.2 缺點
對于對象擴充卡來說,更換擴充卡的實作過程比較複雜。
5.3 适用場景
5.3.1 系統需要使用現有的類,而這些類的接口不符合系統的接口。
5.3.2 想要建立一個可以重用的類,用于與一些彼此之間沒有太大關聯的一些類,包括一些可能在将來引進的類一起工作。
5.3.3 兩個類所做的事情相同或相似,但是具有不同接口的時候。
5.3.4 舊的系統開發的類已經實作了一些功能,但是用戶端卻隻能以另外接口的形式通路,但我們不希望手動更改原有類的時候。
5.3.5 使用第三方元件,元件接口定義和自己定義的不同,不希望修改自己的接口,但是要使用第三方元件接口的功能。
6. 擴充卡應用舉例
6.1 使用過ADO.NET的開發人員應該都用過DataAdapter,它就是用作DataSet和資料源之間的擴充卡。DataAdapter通過映射Fill和Update來提供這一擴充卡。
6.2 手機電源擴充卡
JAVA 代碼實作
Adapter模式的定義:把一個類的接口變換成用戶端所期待的另外一種接口,使得原本由于接口不相容而不能再一起工作的那些類可以一起工作。
擴充卡模式分類:1.類的擴充卡模式(采用繼承實作)2.對象擴充卡(采用對象組合方式實作)
類的擴充卡類圖:
模式的構成:以問題中例子為模型
目标抽象角色(Target):定義客戶所期待要使用的接口,我們把手機當做用戶端,用戶端所需要使用的耳機的接口是2.5的,在這裡就可以抽象出來一個2.5接口的裝置(并不一定是耳機)。
源角色(Adaptee):需要被适配的接口,在這裡指的是我們從市場上買回來的那個3.5接口的耳機。
擴充卡角色(Adapter):用來把源接口轉換成符合要求的目标接口的裝置,在這裡指的是老闆送給我們的那個“轉換器”。
用戶端(Client):這裡指的就是那個給我們帶來麻煩的手機喽。
示例代碼:
Java代碼
- //Target
- package pattern.adapter;
- public interface Target {
- public void provide2_5();
- }
- //Adaptee
- public class Adaptee {
- public void provide3_5(){
- System.out.println("我是一個3.5的接口哦");
- }
- //Adapter
- public class Adapter extends Adaptee implements Target {
- @Override
- public void provide2_5() {
- this.provide3_5();
- //Client
- public class CellPhoneClient {
- public static void main(String[] args) {
- Target target = new Adapter();
- //該手機隻支援2.5接口的耳機
- target.provide2_5();
- }
//Targetpackage pattern.adapter;public interface Target { public void provide2_5();}//Adapteepackage pattern.adapter;public class Adaptee { public void provide3_5(){ System.out.println("我是一個3.5的接口哦"); }}//Adapterpackage pattern.adapter;public class Adapter extends Adaptee implements Target { @Override public void provide2_5() { this.provide3_5(); }}//Clientpackage pattern.adapter;public class CellPhoneClient { public static void main(String[] args) { Target target = new Adapter(); //該手機隻支援2.5接口的耳機 target.provide2_5(); }}
輸出結果
:我是一個3.5的接口哦
從輸出結果可以看出隻支援2.5接口的手機成功的使用3.5的耳機了。這就是擴充卡模式的作用。
對象的擴充卡模式:
對象的擴充卡模式的不同之處在于Adapter角色封裝了Adaptee角色,而不像類的擴充卡模式所采取的繼承方式。其原理基本上是相似的。
應用擴充卡模式的場景:
1.系統需要使用現有的類,而現有類不符合目前系統的要求。如問題的提出。
2.系統要建立一個可以重複使用的類,用來與彼此沒有太大關聯的類或者在将來要引用的類一起工作。在Junit中有使用擴充卡模式的情景。
在TestCase的runBare方法中發現
該方法采用了兩種模式,模闆方法模式(不在本讨論範圍)和擴充卡模式,其中runTest()方法其實 對應的就是我們使用者(程式員)所編寫的測試方法
在runTest方法中通過反射最終調用我們所編寫的測試方法。我們可從宏觀上來分析改代碼,junit作為一個架構,他是沒法知道我們要寫些什麼樣的測試方法的,也是就是說他沒法在runbare方法中直接調用我們所寫的測試方法,他就采用擴充卡模式這樣的一個方式來實作。Junit架構本身沒法直接調用用戶端所寫的測試類,但他可以直接調用他本身擁有的類TestCase,這裡的TestCase就相當于Adapter了,自己所寫了測試類相當于Adaptee角色。
預設的擴充卡模式(Default Adapter):預設的擴充卡模式為一個接口提供預設的實作,子類可以從這個預設的實作類進行擴充,而不必而原有的接口進行擴充。相信大家在學習Swing時“AWT中事件的處理”有所接觸。他的好處在于用戶端不需要去實作與他無關的方法,隻做他最關心的事。
要點:
1. 擴充卡模式主要應用于“希望複用一些現存的類,但是接口又與複用環境要求不一緻的情況”,在遺留代碼複用、類庫遷移等方面非常有用。
2. 擴充卡模式有對象擴充卡和類擴充卡兩種形式的實作結構,但是類擴充卡采用“多繼承”的實作方式,帶來了不良的高耦合,是以一般不推薦使用。對象擴充卡采用“對象組合”的方式,更符合松耦合精神。
實作:
http://images.cnblogs.com/cnblogs_com/god_bless_you/WindowsLiveWriter/b65763e0c958_6D1A/clip_image002_2.jpg類的擴充卡模式結構圖(繼承)
http://images.cnblogs.com/cnblogs_com/god_bless_you/WindowsLiveWriter/b65763e0c958_6D1A/clip_image004_2.jpg對象的擴充卡模式結構圖(組合)
(對象擴充卡的代碼實作)
Target:定義Client使用的與特定領域相關的接口
public interface Target
{
void request();
}
Adaptee:現在需要适配的已經存在的接口
public class Adaptee
{
public void specificRequest(){}
}
Adapter:對Adaptee 的接口與Target接口進行适配
public class Adapter implements Target
{
public Adapter(Adaptee adaptee)
{
super();
this.adaptee = adaptee;
}
public void request()
{
adaptee.specificRequest();
}
private Adaptee adaptee;
}
适用性:
1. 系統需要使用現有的類,而此類的接口不符合系統的需要。
2. 想要建立一個可以重複使用的類,用于與一些彼此之間沒有太大關聯的一些類,包括一些可能在将來引進的類一起工作。這些源類不一定有很複雜的接口。
3. (對對象擴充卡而言)在設計裡,需要改變多個已有子類的接口,如果使用類的擴充卡模式,就要針對每一個子類做一個擴充卡,而這不太實際。
效果及優缺點:
對于類擴充卡:
1. 用一個具體的Adapter類對Adaptee和Taget進行比對。結果是當我們想要比對一個類以及所有它的子類時,類Adapter将不能勝任工作。
2. 使得Adapter可以override(重定義) Adaptee的部分行為,因為Adapter是Adaptee的一個子類。
對于對象擴充卡:
1. 允許一個Adapter與多個Adaptee,即Adaptee本身以及它的所有子類(如果有子類的話)同時工作。Adapter也可以一次給所有的Adaptee添加功能。
2. 使得override(重定義)Adaptee的行為比較困難。如果一定要override Adaptee的方法,就隻好先做一個Adaptee的子類以override Adaptee的方法,然後再把這個子類當作真正的Adaptee源進行适配。