一、基本概念
配接器模式是將某個類的介面轉換成客戶端期望的另一個介面表示,目的是消除由于介面不匹配所造成的的類的兼容性問題,
二、通俗解釋
ADAPTER 配接器模式:在朋友聚會上碰到了一個美女Sarah,從香港來的,可我不會說粵語,她不會說普通話,只好求助于我的朋友kent了,他作為我和Sarah之間的Adapter,讓我和Sarah可以相互交談了(也不知道他會不會耍我) 配接器(變壓器)模式:把一個類的介面變換成客戶端所期待的另一種介面,從而使原本因介面原因不匹配而無法一起作業的兩個類能夠一起作業,適配類可以根據引數返還一個合適的實體給客戶端,
用電器做例子,筆記本電腦的插頭一般都是三相的,即除了陽極、陰極外,還有一個地極,而有些地方的電源插座卻只有兩極,沒有地極,電源插座與筆記本電腦的電源插頭不匹配使得筆記本電腦無法使用,這時候一個三相到兩相的轉換器(配接器)就能解決此問題,而這正像是本模式所做的事情,
三、分類
主要分三類:類的配接器模式、物件的配接器模式、介面的配接器模式,
1. 類的配接器模式:
class Source {
public void method1() {
System.out.println("This is original method...");
}
}
interface Targetable {
/**
* 與原類中的方法相同
*/
public void method1();
/**
* 新類的方法
*/
public void method2();
}
class Adapter extends Source implements Targetable {
@Override
public void method2() {
System.out.println("This is the targetable method...");
}
}
public class AdapterPattern {
public static void main(String[] args) {
Targetable targetable = new Adapter();
targetable.method1();
targetable.method2();
}
}
2. 物件的配接器模式
基本思路和類的配接器模式相同,只是將Adapter 類作修改,這次不繼承Source 類,而是持有Source 類的實體,以達到解決兼容性的問題,
class Source {
public void method1() {
System.out.println("This is original method...");
}
}
interface Targetable {
/**
* 與原類中的方法相同
*/
public void method1();
/**
* 新類的方法
*/
public void method2();
}
class Wrapper implements Targetable {
private Source source;
public Wrapper(Source source) {
super();
this.source = source;
}
@Override
public void method1() {
source.method1();
}
@Override
public void method2() {
System.out.println("This is the targetable method...");
}
}
public class AdapterPattern {
public static void main(String[] args) {
Source source = new Source();
Targetable targetable = new Wrapper(source);
targetable.method1();
targetable.method2();
}
}
3. 介面的配接器模式
介面的配接器是這樣的:有時我們寫的一個介面中有多個抽象方法,當我們寫該介面的實作類時,必須實作該介面的所有方法,這明顯有時比較浪費,因為并不是所有的方法都是我們需要的,有時只需要某一些,此處為了解決這個問題,我們引入了介面的配接器模式,借助于一個抽象類,該抽象類實作了該介面,實作了所有的方法,而我們不和原始的介面打交道,只和該抽象類取得聯系,所以我們寫一個類,繼承該抽象類,重寫我們需要的方法就行,
/**
* 定義埠介面,提供通信服務
*/
interface Port {
/**
* 遠程SSH埠為22
*/
void SSH();
/**
* 網路埠為80
*/
void NET();
/**
* Tomcat容器埠為8080
*/
void Tomcat();
/**
* MySQL資料庫埠為3306
*/
void MySQL();
}
/**
* 定義抽象類實作埠介面,但是什么事情都不做
*/
abstract class Wrapper implements Port {
@Override
public void SSH() {
}
@Override
public void NET() {
}
@Override
public void Tomcat() {
}
@Override
public void MySQL() {
}
}
/**
* 提供聊天服務
* 需要網路功能
*/
class Chat extends Wrapper {
@Override
public void NET() {
System.out.println("Hello World...");
}
}
/**
* 網站服務器
* 需要Tomcat容器,Mysql資料庫,網路服務,遠程服務
*/
class Server extends Wrapper {
@Override
public void SSH() {
System.out.println("Connect success...");
}
@Override
public void NET() {
System.out.println("WWW...");
}
@Override
public void Tomcat() {
System.out.println("Tomcat is running...");
}
@Override
public void MySQL() {
System.out.println("MySQL is running...");
}
}
public class AdapterPattern {
private static Port chatPort = new Chat();
private static Port serverPort = new Server();
public static void main(String[] args) {
// 聊天服務
chatPort.NET();
// 服務器
serverPort.SSH();
serverPort.NET();
serverPort.Tomcat();
serverPort.MySQL();
}
}
介面的配接器模式又稱為預設配接器模式
預設適配(Default Adapter)模式為一個介面提供預設實作,這樣子型別可以從這個預設實作進行擴展,而不必從原有介面進行擴展,作為配接器模式的一個特例,預設是適配模式在JAVA語言中有著特殊的應用,
魯智深的故事
和尚要做什么呢?吃齋、念經、打坐、撞鐘、習武等,如果設計一個和尚介面,給出所有的和尚都需要實作的方法,那么這個介面應當如下:
public interface 和尚 {
public void 吃齋();
public void 念經();
public void 打坐();
public void 撞鐘();
public void 習武();
public String getName();
}
顯然,所有的和尚類都應當實作介面所定義的全部方法,不然就根本通不過JAVA語言編輯器,像下面的魯智深類就不行,
public class 魯智深 implements 和尚{
public void 習武(){
拳打鎮關西;
大鬧五臺山;
大鬧桃花村;
火燒瓦官寺;
倒拔垂楊柳;
}
public String getName(){
return "魯智深";
}
}
由于魯智深只實作了getName()和習武()方法,而沒有實作任何其他的方法,因此,它根本就通不過Java語言編譯器,魯智深類只有實作和尚介面的所有的方法才可以通過Java語言編譯器,但是這樣一來魯智深就不再是魯智深了,以史為鑒,可以知天下,研究一下幾百年前魯智深是怎么剃度成和尚的,會對Java編程有很大的啟發,不錯,當初魯達剃度,眾僧說:“此人形容丑惡、相貌兇頑,不可剃度他",但是長老卻說:”此人上應天星、心地剛直,雖然時下兇頑,命中駁雜,久后卻得清凈,證果非凡,汝等皆不及他,”
原來如此!看來只要這里也應上一個天星的話,問題就解決了!使用面向物件的語言來說,“應”者,實作也;“天星”者,抽象類也,
public abstract class 天星 implements 和尚 {
public void 吃齋(){}
public void 念經(){}
public void 打坐(){}
public void 撞鐘(){}
public void 習武(){}
public String getName(){
return null;
}
}
魯智深類繼承抽象類“天星”
public class 魯智深 extends 和尚{
public void 習武(){
拳打鎮關西;
大鬧五臺山;
大鬧桃花村;
火燒瓦官寺;
倒拔垂楊柳;
}
public String getName(){
return "魯智深";
}
}
這個抽象的天星類便是一個配接器類,魯智深實際上借助于配接器模式達到了剃度的目的,此配接器類實作了和尚介面所要求的所有方法,但是與通常的配接器模式不同的是,此配接器類給出的所有的方法的實作都是“平庸”的,這種“平庸化”的配接器模式稱作預設適配模式,
在很多情況下,必須讓一個具體類實作某一個介面,但是這個類又用不到介面所規定的所有的方法,通常的處理方法是,這個具體類要實作所有的方法,那些有用的方法要有實作,那些沒有用的方法也要有空的、平庸的實作,
這些空的方法是一種浪費,有時也是一種混亂,除非看過這些空方法的代碼,程式員可能會以為這些方法不是空的,即便他知道其中有一些方法是空的,也不一定知道哪些方法是空的,哪些方法不是空的,除非看過這些方法的源代碼或是檔案,
預設適配模式可以很好的處理這一情況,可以設計一個抽象的配接器類實作介面,此抽象類要給介面所要求的每一種方法都提供一個空的方法,就像幫助了魯智深的“上應天星”一樣,此抽象類可以使它的具體子類免于被迫實作空的方法,
四、類配接器和物件配接器的權衡
- 類配接器使用物件繼承的方式,是靜態的定義方式;而物件配接器使用物件組合的方式,是動態組合的方式,
-
對于類配接器由于配接器直接繼承了Adaptee,使得配接器不能和Adaptee的子類一起作業,因為繼承是靜態的關系,當配接器繼承了Adaptee后,就不可能再去處理 Adaptee的子類了,
-
對于物件配接器一個配接器可以把多種不同的源適配到同一個目標,換言之,同一個配接器可以把源類和它的子類都適配到目標介面,因為物件配接器采用的是物件組合的關系,只要物件型別正確,是不是子類都無所謂,
-
對于類配接器配接器可以重定義Adaptee的部分行為,相當于子類覆寫父類的部分實作方法,
-
對于物件配接器要重定義Adaptee的行為比較困難,這種情況下,需要定義Adaptee的子類來實作重定義,然后讓配接器組合子類,雖然重定義Adaptee的行為比較困難,但是想要增加一些新的行為則方便的很,而且新增加的行為可同時適用于所有的源,
-
對于類配接器,僅僅引入了一個物件,并不需要額外的參考來間接得到Adaptee,
-
對于物件配接器,需要額外的參考來間接得到Adaptee,
建議盡量使用物件配接器的實作方式,多用合成或聚合、少用繼承,當然,具體問題具體分析,根據需要來選用實作方式,最適合的才是最好的,
配接器模式的優點
-
更好的復用性:系統需要使用現有的類,而此類的介面不符合系統的需要,那么通過配接器模式就可以讓這些功能得到更好的復用,
-
更好的擴展性:在實作配接器功能的時候,可以呼叫自己開發的功能,從而自然地擴展系統的功能,
配接器模式的缺點
過多的使用配接器,會讓系統非常零亂,不易整體進行把握,比如,明明看到呼叫的是A介面,其實內部被適配成了B介面的實作,一個系統如果太多出現這種情況,無異于一場災難,因此如果不是很有必要,可以不使用配接器,而是直接對系統進行重構,
配接器模式的用意是要改變源的介面,以便于目標介面相容,預設適配的用意稍有不同,它是為了方便建立一個不平庸的配接器類而提供的一種平庸實作,
在任何時候,如果不準備實作一個介面的所有方法時,就可以使用“預設適配模式”制造一個抽象類,給出所有方法的平庸的具體實作,這樣,從這個抽象類再繼承下去的子類就不必實作所有的方法了,
(參考:《JAVA與模式》之配接器模式、Java 中幾種常用設計模式、java常用的設計模式)
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/8568.html
標籤:設計模式
上一篇:單例模式
