定義:配接器模式是將一個類的介面轉換成客戶希望的另一個介面,配接器模式使得原本由于介面不兼容而不能一起作業的類可以一起作業,在軟體設計中我們需要將一些“現存的物件”放到新的環境中,而新環境要求的介面是現物件所不能滿足的,我們可以使用這種模式進行介面適配轉換,使得“老物件”符合新環境的要求,
使用場景:1、系統需要使用現有的類,而此類的介面不符合系統的需要;2、通過介面轉換,將一個類插入另一個類中,用電器來打個比喻:有一個電器的插頭是三腳的,而現有的插座是兩孔的,要使插頭插上插座,我們需要一個插頭轉換器,這個轉換器即是配接器,
配接器模式涉及三個角色:
1、源(Adaptee):需要被適配的物件或型別,相當于插頭;
2、配接器(Adapter):連接源和目標物件的中間物件,相當于轉換器;
3、目標角色(Target):定義了客戶端期望的介面,相當于插座;
如何實作呢?
使用繼承(類配接器)或者聚合(物件配接器)已有的物件實作想要的目標介面,優先推薦使用物件配接器(基于組合優先于繼承),
配接器模式結構圖:

以下我們先以物件適配為例來分析,以下是原始碼:
package cn.com.pep.model.adapter.a1;
/**
*
* @Title: AdvancedMediaPlayer
* @Description: 源物件的介面
* @author wwh
* @date 2022-9-5 10:10:14
*/
public interface AdvancedMediaPlayer {
/**
* @Title: playVlc
* @Description:
* @param filename
*/
public void playVlc(String filename);
/**
* @Title: playMp4
* @Description:
* @param filename
*/
public void playMp4(String filename);
}
package cn.com.pep.model.adapter.a1; /** * * @Title: MeidaPlayer * @Description: 目標物件介面 * @author wwh * @date 2022-9-5 10:08:30 */ public interface MeidaPlayer { /** * @Title: play * @Description: * @param audioType * @param filename */ public void play(String audioType,String filename); }
package cn.com.pep.model.adapter.a1;
/**
*
* @Title: Mp4MediaPlayer
* @Description: 源物件
* @author wwh
* @date 2022-9-5 10:13:48
*/
public class Mp4MediaPlayer implements AdvancedMediaPlayer {
@Override
public void playVlc(String filename) {
}
@Override
public void playMp4(String filename) {
System.err.println("Playing mp4 and filename is:" + filename);
}
}
package cn.com.pep.model.adapter.a1; /** * * @Title: VLCMediaPlayer * @Description: 源物件 * @author wwh * @date 2022-9-5 10:12:07 */ public class VLCMediaPlayer implements AdvancedMediaPlayer { @Override public void playVlc(String filename) { System.err.println("Playing vlc and filename is:" + filename); } @Override public void playMp4(String filename) { } }
package cn.com.pep.model.adapter.a1; /** * * @Title: MediaPlayerAdapter * @Description: 物件配接器,通過聚合持有一個源物件的參考 * @author wwh * @date 2022-9-5 10:19:10 */ public class MediaPlayerAdapter implements MeidaPlayer { /** * 通過聚合的方式持有一個源物件的參考 */ private AdvancedMediaPlayer player; public MediaPlayerAdapter(String filetype) { if ("mp4".equalsIgnoreCase(filetype)) { player = new Mp4MediaPlayer(); } else if ("vlc".equalsIgnoreCase(filetype)) { player = new VLCMediaPlayer(); } } @Override public void play(String audioType, String filename) { System.err.println("執行了配接器中的play()方法"); if ("mp4".equalsIgnoreCase(audioType)) { player.playMp4(filename); } else if ("vlc".equalsIgnoreCase(audioType)) { player.playVlc(filename); } } }
package cn.com.pep.model.adapter.a1; /** * @Title: AudioMediaPlayer * @Description: 目標物件 * @author wwh * @date 2022-9-5 10:49:33 */ public class AudioMediaPlayer implements MeidaPlayer { private MediaPlayerAdapter adapter; @Override public void play(String audioType, String filename) { if ("mp3".equalsIgnoreCase(audioType)) { System.out.println("Playing mp3 and filename is: " + filename); } else if ("mp4".equalsIgnoreCase(audioType) || "vlc".equalsIgnoreCase(audioType)) { adapter = new MediaPlayerAdapter(audioType); adapter.play(audioType, filename); } } }
package cn.com.pep.model.adapter.a1; /** * * @Title: AdapterPatternDemo * @Description: 測驗代碼 * @author wwh * @date 2022-9-5 10:56:33 */ public class AdapterPatternDemo { public static void main(String[] args) { AudioMediaPlayer player = new AudioMediaPlayer(); player.play("mp3", "紅日.mp3"); player.play("mp4", "天下無賊.mp4"); player.play("vlc", "平凡的世界.vlc"); } }
UML類圖:

以上就是物件適配模式,目標介面MediaPlayer中有一個play(String,String)方法,而源物件介面AdvancedMediaPlayer中并沒有這個方法,我們想通過目標介面的物件AudioMediaPlayer實作播放VCL、Mp4格式的檔案,就需要一個MediaPlayerAdapter來對源物件(Mp4MediaPlayer、VLCMedaiPlayer)進行適配,它通過聚合的方式持有一個AdvancedMediaPlayer型別的參考,并且它還實作了與目標物件相同的介面,自然也就包含了和目標物件相同的方法,目標物件AudioMediaPlayer關聯了MediaPlayerAdapter物件的參考,當然就可以實作播放VCL、Mp4格式的檔案了;
接下來我們說說類配接器模式:
類適配是通過Adapter類繼承Adaptee(被適配類),同時實作Target介面(因為Java不支持多繼承,所以只能通過實作介面的方式來實作多繼承)來實作的,類配接器的重點在于類,是通過構造一個繼承Adaptee類來實作配接器的功能的,而上面提到的物件配接器重點在于物件,是通過直接在Adapter中聚合Adaptee類來實作的,當需要呼叫特殊功能的時候,直接使用Adapter中聚合的那個Adaptee物件來呼叫特殊的功能即可;
以下是類配接器的測驗代碼:
package cn.com.pep.model.adapter.a2; /** * * @Title: Adaptee * @Description: 需要適配的類 * @author wwh * @date 2022-9-5 15:29:20 */ public class Adaptee { public void specificRequest() { System.err.println("執行適配類的方法"); } }
package cn.com.pep.model.adapter.a2; /** * * @Title: Target * @Description: 目標物件的介面 * @author wwh * @date 2022-9-5 15:31:05 */ public interface Target { /** * @Title: calculate * @Description: */ public void calculate(); }
package cn.com.pep.model.adapter.a2; /** * * @Title: ConcreteTarget * @Description: 目標物件 * @author wwh * @date 2022-9-5 15:49:50 */ public class ConcreteTarget implements Target{ @Override public void calculate() { System.err.println("執行目標類的方法"); } }
package cn.com.pep.model.adapter.a2; /** * * @Title: Adapter * @Description: 類配接器,繼承了需要適配的類,并且實作了目標物件的介面 * @author wwh * @date 2022-9-5 15:59:37 */ public class Adapter extends Adaptee implements Target{ @Override public void calculate() { specificRequest(); } }
package cn.com.pep.model.adapter.a2; /** * * @Title: ClassAdapterDemo * @Description: 測驗類 * @author wwh * @date 2022-9-5 15:57:58 */ public class ClassAdapterDemo { public static void main(String[] args) { // 使用普通功能類 Target concreteTarget = new ConcreteTarget();// 實體化一個普通類 concreteTarget.calculate(); // 使用特殊功能類,即適配類 Target adapter = new Adapter(); adapter.calculate(); } }
類適配和物件適配的比較:
1、類適配使用繼承,是靜態定義的;而物件適配采用的是物件聚合的方式,是動態定義的;
2、對于類配接器,由于配接器Adapter直接繼承了Adaptee,使得Adapter不能和Adaptee的子類一起作業,因為繼承是靜態關系,當配接器繼承了Adaptee之后,就不可能再去處理Adaptee的子類了;
3、對于物件配接器,一個配接器可以把多種不同的源適配到同一個目標,換言之,同一個配接器可以把源類和它的子類都適配到目標介面,因為物件配接器采用的是物件聚合的方式,只要型別正確,是不是子類都無所謂;
4、對于類適配,配接器可以重新定義Adaptee的部分行為,相當于子類覆寫父類的部分方法實作;
5、對于物件配接器,想要直接重新定義Adaptee的行為比較困難,我們可以通過一個Adaptee的子類來重新定義Adaptee的行為,然后讓配接器聚合這個子類來完成Adaptee類行為的重新定義;
配接器模式的優缺點:
1、更好的復用,系統需要使用現有類的時候,而此類的介面不符合系統的要求,我們就可以使用配接器模式讓這些功能得到更好的復用;
2、更好的擴展性,在實作配接器功能的時候,可以呼叫自己開發的功能,從而自然地擴展系統的功能;
3、過多的使用配接器,會讓系統非常零亂,不易整體進行把握,比如,明明看到呼叫的是A介面,其實內部被適配成了B介面的實作,一個系統如果太多出現這種情況,無異于一場災難,因此如果不是很有必要,可以不使用配接器,而是直接對系統進行重構,
建議盡量使用物件配接器的實作方式,多用合聚合/組合、少用繼承,當然,具體問題具體分析,根據需要來選用實作方式,最適合的才是最好的,配接器不是在詳細設計時添加的,而是解決正在服役的專案的問題,
本文來自博客園,作者:一只烤鴨朝北走,僅用于技術學習,所有資源都來源于網路,部分是轉發,部分是個人總結,歡迎共同學習和轉載,轉載請在醒目位置標明原文,如有侵權,請留言告知,及時撤除,轉載請注明原文鏈接:https://www.cnblogs.com/wha6239/p/16657461.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/504500.html
標籤:設計模式
