PS:转载请注明出处
做者: TigerChain
地址: http://www.jianshu.com/p/1edf5d944abb
本文出自 TigerChain 简书 人人都会设计模式设计模式教程简介ide
正文源码分析
好比电脑转接器「这里主要是指链接电脑和投影仪的」,以个人 MAC 电脑为例子,咱们公司的投影支持 VGA 和 HDMI ,可是个人 MAC 电脑只有一个 MINI DP 接口,如何把 MINI DP 转成 VGA 或 HDMI ,那么我就买了这个玩意「全称 MINI DP 转 VGA & HDMI 适配器」,这东西就是一个适配器测试
这个适配器就能够把 MAC 和有 VGA 或 HDMI 的设备链接起来了,以下:this
相似的还有电脑电源适配器,变压器「也是一种适配器」,其实净水器也能够看做是一种适配器「把杂水变成纯净水」,等等spa
好比咱们对接第三方的接口到咱们的系统「对方给咱们的接口,咱们如今的接口对接不起来」翻译
咱们就须要写一个中间层「适配器」,作为一个桥梁,把两个接口链接起来设计
适配器模式的定义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<T>,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<T> 就是把 List<T> 的数据源采用一系列方法转化成 ListAdapter 须要的几种方法 getView getPosition 等等「这就是一个适配的过程」
ListAdapter 既是策略模式又是适配器模式
根据选择模式使用那种适配器 ListAdapter 就是策略模式,可是根据每一个策略所实现功能「它就是适配器模式」
优势
缺点
到此为止,咱们就介绍完了适配器模式,点赞是一种美德
公众号:TigerChain 更多精彩的文章等着你