[TOC]
轉:https://www.cnblogs.com/java-my-life/archive/2012/04/13/2442795.html
# 1.基礎概念
1. **適配器模式把一個類的接口變換成客戶端所期待的另一種接口,從而使原本因接口不匹配而無法在一起工作的兩個類能夠在一起工作。**
2. 適配器模式有**類的適配器模式**和**對象的適配器模式**兩種不同的形式。
用電器做例子,筆記本電腦的插頭一般都是三相的,即除了陽極、陰極外,還有一個地極。而有些地方的電源插座卻只有兩極,沒有地極。電源插座與筆記本電腦的電源插頭不匹配使得筆記本電腦無法使用。這時候一個三相到兩相的轉換器(適配器)就能解決此問題,而這正像是本模式所做的事情。
## 1.1 類適配器模式
類的適配器模式把適配的類的API轉換成為目標類的API。

在上圖中可以看出,Adaptee類并沒有sampleOperation2()方法,而客戶端則期待這個方法。為使客戶端能夠使用Adaptee類,提供一個中間環節,即類Adapter,把Adaptee的API與Target類的API銜接起來。Adapter與Adaptee是繼承關系,這決定了這個適配器模式是類的:
> 1. 目標(Target)角色:
> 這就是所期待得到的接口。注意:由于這里討論的是類適配器模式,因此目標不可以是類。
> 2. .源(Adapee)角色:
> 現在需要適配的接口。
> 3. 適配器(Adaper)角色:
> 適配器類是本模式的核心。適配器把源接口轉換成目標接口。顯然,這一角色不可以是接口,而必須是具體類。
例子:
```
public interface Target {
/**
* 這是源類Adaptee也有的方法
*/
public void sampleOperation1();
/**
* 這是源類Adapteee沒有的方法
*/
public void sampleOperation2();
}
```
上面給出的是目標角色的源代碼,這個角色是以一個JAVA接口的形式實現的。可以看出,這個接口聲明了兩個方法:sampleOperation1()和sampleOperation2()。而源角色Adaptee是一個具體類,它有一個sampleOperation1()方法,但是沒有sampleOperation2()方法。
```
public class Adaptee {
public void sampleOperation1(){}
}
```
適配器角色Adapter擴展了Adaptee,同時又實現了目標(Target)接口。由于Adaptee沒有提供sampleOperation2()方法,而目標接口又要求這個方法,因此適配器角色Adapter實現了這個方法。
```
public class Adapter extends Adaptee implements Target {
/**
* 由于源類Adaptee沒有方法sampleOperation2()
* 因此適配器補充上這個方法
*/
@Override
public void sampleOperation2() {
//寫相關的代碼
}
}
```
## 1.2對象適配器模式
與類的適配器模式一樣,對象的適配器模式把被適配的類的API轉換成為目標類的API,與類的適配器模式不同的是,對象的適配器模式不是使用繼承關系連接到Adaptee類,而是使用委派關系連接到Adaptee類。

從上圖可以看出,Adaptee類并沒有sampleOperation2()方法,而客戶端則期待這個方法。為使客戶端能夠使用Adaptee類,需要提供一個包裝(Wrapper)類Adapter。這個包裝類包裝了一個Adaptee的實例,從而此包裝類能夠把Adaptee的API與Target類的API銜接起來。Adapter與Adaptee是委派關系,這決定了適配器模式是對象的。
```
public interface Target {
/**
* 這是源類Adaptee也有的方法
*/
public void sampleOperation1();
/**
* 這是源類Adapteee沒有的方法
*/
public void sampleOperation2();
}
```
~~~
public class Adaptee {
public void sampleOperation1(){}
}
~~~
[](javascript:void(0); "復制代碼")
[](javascript:void(0); "復制代碼")
~~~
public class Adapter {
private Adaptee adaptee;
public Adapter(Adaptee adaptee){
this.adaptee = adaptee;
}
/**
* 源類Adaptee有方法sampleOperation1
* 因此適配器類直接委派即可
*/
public void sampleOperation1(){
this.adaptee.sampleOperation1();
}
/**
* 源類Adaptee沒有方法sampleOperation2
* 因此由適配器類需要補充此方法
*/
public void sampleOperation2(){
//寫相關的代碼
}
}
~~~
[](javascript:void(0); "復制代碼")
### 類適配器和對象適配器的權衡
● **類適配器**使用對象繼承的方式,是靜態的定義方式;而**對象適配器**使用對象組合的方式,是動態組合的方式。
● **對于類適配器**,由于適配器直接繼承了Adaptee,使得適配器不能和Adaptee的子類一起工作,因為繼承是靜態的關系,當適配器繼承了Adaptee后,就不可能再去處理? Adaptee的子類了。
**對于對象適配器**,一個適配器可以把多種不同的源適配到同一個目標。換言之,同一個適配器可以把源類和它的子類都適配到目標接口。因為對象適配器采用的是對象組合的關系,只要對象類型正確,是不是子類都無所謂。
● ? **對于類適配器**,適配器可以重定義Adaptee的部分行為,相當于子類覆蓋父類的部分實現方法。
**對于對象適配器**,要重定義Adaptee的行為比較困難,這種情況下,需要定義Adaptee的子類來實現重定義,然后讓適配器組合子類。雖然重定義Adaptee的行為比較困難,但是想要增加一些新的行為則方便的很,而且新增加的行為可同時適用于所有的源。
● **對于類適配器**,僅僅引入了一個對象,并不需要額外的引用來間接得到Adaptee。
**對于對象適配器**,需要額外的引用來間接得到Adaptee。
建議盡量使用對象適配器的實現方式,多用合成/聚合、少用繼承。當然,具體問題具體分析,根據需要來選用實現方式,最適合的才是最好的。
## 適配器模式的優點
* ** 更好的復用性**
系統需要使用現有的類,而此類的接口不符合系統的需要。那么通過適配器模式就可以讓這些功能得到更好的復用。
* ** 更好的擴展性**
在實現適配器功能的時候,可以調用自己開發的功能,從而自然地擴展系統的功能。
## 適配器模式的缺點
過多的使用適配器,會讓系統非常零亂,不易整體進行把握。比如,明明看到調用的是A接口,其實內部被適配成了B接口的實現,一個系統如果太多出現這種情況,無異于一場災難。因此如果不是很有必要,可以不使用適配器,而是直接對系統進行重構。
- 計算機網絡
- 基礎_01
- tcp/ip
- http轉https
- Let's Encrypt免費ssl證書(基于haproxy負載)
- what's the http?
- 網關
- 網絡IO
- http
- 工具
- Git
- 初始本地倉庫并上傳
- git保存密碼
- Gitflow
- maven
- 1.生命周期命令
- 聚合與繼承
- 插件管理
- assembly
- 資源管理插件
- 依賴范圍
- 分環境打包
- dependencyManagement
- 版本分類
- 找不到主類
- 無法加載主類
- 私服
- svn
- gradle
- 手動引入第三方jar包
- 打包exe文件
- Windows
- java
- 設計模式
- 七大原則
- 1.開閉原則
- 2. 里式替換原則
- 3. 依賴倒置原則
- 4. 單一職責原則
- 單例模式
- 工廠模式
- 簡單工廠
- 工廠方法模式
- 抽象工廠模式
- 觀察者模式
- 適配器模式
- 建造者模式
- 代理模式
- 適配器模式
- 命令模式
- json
- jackson
- poi
- excel
- easy-poi
- 規則
- 模板
- 合并單元格
- word
- 讀取
- java基礎
- 類路徑與jar
- 訪問控制權限
- 類加載
- 注解
- 異常處理
- String不可變
- 跨域
- transient關鍵字
- 二進制編碼
- 泛型1
- 與或非
- final詳解
- Java -jar
- 正則
- 讀取jar
- map
- map計算
- hashcode計算原理
- 枚舉
- 序列化
- URLClassLoader
- 環境變量和系統變量
- java高級
- java8
- 1.Lambda表達式和函數式接口
- 2.接口的默認方法和靜態方法
- 3.方法引用
- 4.重復注解
- 5.類型推斷
- 6.拓寬注解的應用場景
- java7-自動關閉資源機制
- 泛型
- stream
- 時區的正確理解
- StringJoiner字符串拼接
- 注解
- @RequestParam和@RequestBody的區別
- 多線程
- 概念
- 線程實現方法
- 守護線程
- 線程阻塞
- 筆試題
- 類加載
- FutureTask和Future
- 線程池
- 同步與異步
- 高效簡潔的代碼
- IO
- ThreadLocal
- IO
- NIO
- 圖片操作
- KeyTool生成證書
- 壓縮圖片
- restful
- 分布式session
- app保持session
- ClassLoader.getResources 能搜索到的資源路徑
- java開發規范
- jvm
- 高并發
- netty
- 多線程與多路復用
- 異步與事件驅動
- 五種IO模型
- copy on write
- code style
- 布隆過濾器
- 筆試
- 數據庫
- mybatis
- mybatis與springboot整合配置
- pagehelper
- 分頁數據重復問題
- Java與數據庫之間映射
- 攔截器
- 攔截器應用
- jvm
- 堆內存測試
- 線程棧
- 直接內存
- 內存結構
- 內存模型
- 垃圾回收
- 調優
- 符號引用
- 運行參數
- 方法區
- 分帶回收理論
- 快捷開發
- idea插件
- 注釋模板
- git
- pull沖突
- push沖突
- Excel處理
- 圖片處理
- 合并單元格
- easypoi
- 模板處理
- 響應式編程
- reactor
- reactor基礎
- jingyan
- 規范
- 數據庫