版權聲明:本文爲博主原創文章,未經博主容許不得轉載設計模式
PS:轉載請註明出處
做者: TigerChain
地址: www.jianshu.com/p/1edf5d944…
本文出自 TigerChain 簡書 人人都會設計模式ide
教程簡介源碼分析
正文測試
好比電腦轉接器「這裏主要是指鏈接電腦和投影儀的」,以個人 MAC 電腦爲例子,咱們公司的投影支持 VGA 和 HDMI ,可是個人 MAC 電腦只有一個 MINI DP 接口,如何把 MINI DP 轉成 VGA 或 HDMI ,那麼我就賣了這個玩意「全稱 MINI DP 轉 VGA & HDMI 適配器」,這東西就是一個適配器this
這個適配器就能夠把 MAC 和有 VGA 或 HDMI 的設備鏈接起來了,以下:spa
相似的還有電腦電源適配器,變壓器「也是一種適配器」,其實淨水器也能夠看做是一種適配器「把雜水變成純淨水」,等等翻譯
好比咱們對接第三方的接口到咱們的系統「對方給咱們的接口,咱們如今的接口對接不起來」設計
咱們就須要寫一箇中間層「適配器」,作爲一個橋樑,把兩個接口鏈接起來3d
適配器模式的定義 code
通俗的說適配器模式就是把兩個不兼容的接口鏈接起來,相似一個橋樑的做用
注:適配器模式類比一個橋樑做用「它的做用不只僅鏈接這麼簡單,還有轉化等操做,橋樑就是爲了方便理解」
適配器模式的結構
角色 | 類別 | 說明 |
---|---|---|
Target | 目標角色 | 是一個接口,也就是咱們期待要轉化成的接口 |
Adaptee | 源角色 | 原始的類或接口對象 |
Adapter | 適配器角色 | 把源角色轉化成目標角色的類 |
適配器模式的分類
類適配器簡單的 UML
總結一下就是:適配器「Adapter」繼承源類「Src」而且實現目標「Dst」接口,來實現 Src-->Dst 的轉換
對象適配器簡單的 UML
總結一下就是:適配器「Adapter」持有源類「Src」的引用,並實現目標「Dst」接口,來實現 Src--> Dst 的轉化
對於這種模式「資料上也沒有說有這種模式,我是在寫代碼的過程當中發現能夠這樣寫」,我持保留意見,若是有什麼問題,你們徹底能夠說適配器模式的分類就有以上兩種模式,可我認爲這是適配器模式的一個變種
接口適配器簡單的 UML
*總結一下就是: 適配器實現源和目標,把源轉化成目標這麼一個過程
一、Mac 電腦鏈接投影儀適配器
以開頭的例子爲例子, MAC 電腦要鏈接投影儀器,須要一個 MINI DP 轉 VGA & HDMI 適配器,而後才能鏈接上投影儀
因此這裏目標是 VGAORHDMI ,源是 MINI DP 適配器就是上面的那根線
類適配投影儀和 MAC 電腦簡單的 UML
根據 UML 擼碼
使用類適配器模式
/** * 目標角色,對投影儀來講就要 VAG 或 HDMI * @auther TigerChain */
public interface VgaOrHdmi {
/**輸出 VGA 或是 Hdmi 接口*/
String getVgaOrHdmi() ;
}複製代碼
/** * 源角色,MAC 電腦上的 MINIDP 接口 * @auther TigerChain */
public class MiniDp {
public String outPutMinkDp(){
return "我是 mac 上的 MiniDp 輸入接口" ;
}
}複製代碼
/** * 適配器,既是 MINIDP 接口也是 VAGORHDMI 接口,這樣就能夠把 MINIDP 轉成 * VAG OR HDMI 接口 * @auther TigerChain */
public class MidiDp2VgaOrHdmiAdapter extends MiniDp implements VgaOrHdmi{
@Override
public String getVgaOrHdmi() {
return convertMiniDp2VgaOrHdmi() ;
}
/** * 把 MINIDP 轉化成 VAG 或 HDMI 方法 * @return */
private String convertMiniDp2VgaOrHdmi(){
//拿到源
String str = outPutMinkDp();
System.out.println(str+" \n 通過適配器轉化 ");
// 爲這簡單起見,這裏直接修改源
str = "輸出變成 VGA 和 HDMI 接口" ;
return str ;
}
}複製代碼
/** * 這是投影儀,我就是 VGA 和 HDMI 接口的 * @auther TigerChain */
public class Projector {
// 我要的就是 VGA 或者 HDMI 接口
public String getVgaOrHdmi(VgaOrHdmi vgaOrHdmi){
return vgaOrHdmi.getVgaOrHdmi() ;
}
}複製代碼
/** * 測試類 * @auther TigerChain */
public class Test {
public static void main(String args[]){
//投影儀
Projector projector = new Projector() ;
//適配器
VgaOrHdmi adapter = new MidiDp2VgaOrHdmiAdapter() ;
// 最後獲得投影儀想要的 VAG or HDMI 便可
String str = projector.getVgaOrHdmi(adapter) ;
System.out.println(str);
}
}複製代碼
完美轉化了有木有
對象適配器實現上述例子
對象適配投影儀和 MAC 電腦簡單的 UML
是否是和上面的圖同樣?錯,確定不同,同樣我還貼出來「我又沒病」,只有一點改變,就是適配器不是繼承源,而是持有源的引用,代碼修改起來很是簡單,只是修改適配器便可「別的代碼都是同樣的」
/** * 適配器,既是 MINIDP 接口也是 VAGORHDMI 接口,這樣就能夠把 MINIDP 轉成 * VAG OR HDMI 接口 */
public class MidiDp2VgaOrHdmiAdapter implements VgaOrHdmi{
// 修改之處 1
private MiniDp miniDp ;
// 修改之處 2
public MidiDp2VgaOrHdmiAdapter(MiniDp miniDp){
this.miniDp = miniDp ;
}
@Override
public String getVgaOrHdmi() {
return convertMiniDp2VgaOrHdmi() ;
}
/** * 把 MINIDP 轉化成 VAG 或 HDMI 方法 * @return */
private String convertMiniDp2VgaOrHdmi(){
// 修改之處 3 拿到源
String str = miniDp.outPutMinkDp();
System.out.println(str+" \n 通過適配器轉化 ");
// 爲這簡單起見,這裏直接修改源
str = "輸出變成 VGA 和 HDMI 接口" ;
return str ;
}
}複製代碼
修改測試類
結果和上面是同樣的
適配器模式通常狀況下不是軟件設計的時候就要考慮的一種模式,它是一種隨着軟件的維護可能因爲不一樣的開發人員,不一樣的產品,不一樣的廠家形成的功能相似而接口不相同的狀況下一種解決方案「只有碰到沒法改變原有設計和代碼的狀況下,才考慮適配」
二、成龍初探好萊塢
咱們的功夫明星成龍初闖好萊塢的時候有一個最大的障礙就是語言問題「英文不太熟悉」,那麼最先的時候都是有翻譯者的,那麼這個翻譯員就充當了適配器的角色「把英文翻譯成中文,或者把中文翻譯成英文」
翻譯員簡單的 UML
根據 UML 擼代碼
/** * Created by tigerchain on 11/12/17. */
public interface ISpeakEn {
// 說英文
String speakEnglish(String str) ;
}複製代碼
/** * Created by tigerchain on 11/12/17. */
public interface ISpeakCn {
// 說中文
String speakCn(String str) ;
}複製代碼
/** * Created by tigerchain on 11/12/17. * 翻譯的接口 */
public interface Interpreter {
// 中文翻譯成英文
void chinese2English(String str) ;
// 英文翻譯成中文
void english2Chinese(String str) ;
}複製代碼
/** * Created by tigerchain on 11/12/17. * 舉個例子,成龍有一個張翻譯,能把英文翻譯成中文,也能把中文翻譯成英文 */
public class ZhangTranslation implements Iinterpreter,ISpeakCn,ISpeakEn{
@Override
public void chinese2English(String str) {
translationC2E(speakCn(str));
}
@Override
public void english2Chinese(String str) {
translationE2C(str) ;
}
// 翻譯英文--> 中文
private void translationE2C(String str) {
System.out.println("小張把 "+str+" 翻譯成中文");
}
// 翻譯中文--> 英文
private void translationC2E(String str){
System.out.println("小張把 "+str+" 翻譯成英文");
}
@Override
public String speakCn(String str) {
return str ;
}
@Override
public String speakEnglish(String str) {
return str;
}
}複製代碼
/** * Created by tigerchain on 11/12/17. * 一個老外用英文給成龍打招呼 */
public class Foreigner implements ISpeakEn {
@Override
public String speakEnglish(String str) {
String say = "Wills say:"+str ;
System.out.println(say);
return say ;
}
}複製代碼
/** * Created by tigerchain on 11/12/17. */
public class JackieChan implements ISpeakCn {
@Override
public String speakCn(String str) {
String say = "成龍說:"+str ;
System.out.println(say);
return say ;
}
}複製代碼
/** * Created by tigerchain on 11/12/17. * 這是一個成龍對話老外的測試類 */
public class Test {
public static void main(String args[]){
// 成龍說了一句話
JackieChan jackieChan = new JackieChan() ;
String str = jackieChan.speakCn("你好 wills");
// 老外說了一句
Foreigner foreigner = new Foreigner() ;
String str2 = foreigner.speakEnglish("Hello Jackie Chain");
// 張翻譯翻譯
ZhangTranslation zhangTranslation = new ZhangTranslation() ;
zhangTranslation.chinese2English(str);
zhangTranslation.english2Chinese(str2);
}
}複製代碼
怎麼樣這個張翻譯「適配器」還不錯吧,固然適配器模式也會進化,會變種,可是萬變不離其宗「上面 Demo 就能夠看做是一個變種的適配器模式」
ListAdapter
沒有搞錯吧,上一節不是說了 ListAdapter 是一種策略模式嗎?沒錯它也是一種適配器模式「從名字就能夠看出來」
ListAdapter 適配器簡單的 UML
從上圖能夠看出,BaseAdapter 是一個基礎適配器,下面子類是具體各自的適配器,這些適配器的做用就是把數據 List,Cusor 等轉化成 ListAdapter 接口,最終讓客戶端 ListView 來調用「能夠通俗的說就是把數據適配到 View 上面」
以 ArrayAdapter<T> 源碼分析一下
public interface Adapter {
int getCount();
Object getItem(int var1);
long getItemId(int var1);
boolean hasStableIds();
View getView(int var1, View var2, ViewGroup var3);
int getItemViewType(int var1);
int getViewTypeCount();
boolean isEmpty();
}複製代碼
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter{
//省略苦幹代碼
public boolean areAllItemsEnabled() {
return true;
}
public boolean isEnabled(int position) {
return true;
}
public View getDropDownView(int position, View convertView, ViewGroup parent) {
return getView(position, convertView, parent);
}
public int getItemViewType(int position) {
return 0;
}
public int getViewTypeCount() {
return 1;
}
public boolean isEmpty() {
return getCount() == 0;
}
}複製代碼
public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSpinnerAdapter {
private List<T> mObjects;
//列出其中一個構造方法
public ArrayAdapter(@NonNull Context context, @LayoutRes int resource, @IdRes int textViewResourceId) {
this(context, resource, textViewResourceId, new ArrayList<>());
}
@Override
public int getCount() {
return mObjects.size();
}
@Override
public @Nullable T getItem(int position) {
return mObjects.get(position);
}
public int getPosition(@Nullable T item) {
return mObjects.indexOf(item);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public @NonNull View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
return createViewFromResource(mInflater, position, convertView, parent, mResource);
}
//其它代碼流省略
}複製代碼
咱們經過源碼能夠看到 ArrayAdapter 就是把 List 的數據源採用一系列方法轉化成 ListAdapter 須要的幾種方法 getView getPosition 等等「這就是一個適配的過程」
ListAdapter 既是策略模式又是適配器模式
根據選擇模式使用那種適配器 ListAdapter 就是策略模式,可是根據每一個策略所實現功能「它就是適配器模式」
優勢
缺點
到此爲止,咱們就介紹完了適配器模式,點贊是一種美德