适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
上面这个图可以很容易的看出适配器的作用。
适配器模式有类的适配器模式和对象的适配器模式两种不同的形式。
类适配器模式
类的适配器模式把适配的类的API转换成为目标类的API。
在上图中可以看出,Adaptee类并没有sampleOperation2()方法,而客户端则期待这个方法。为使客户端能够使用Adaptee类,提供一个中间环节,即类Adapter,把Adaptee的API与Target类的API衔接起来。Adapter与Adaptee是继承关系,这决定了这个适配器模式是类的,Adapter与Target是一种实现关系。
模式所涉及的角色有:
public interface Target { /** * 这是源类Adaptee也有的方法 */ public void sampleOperation1(); /** * 这是源类Adapteee没有的方法 */ public void sampleOperation2(); }
public class Adaptee { public void sampleOperation1(){} }适配器角色Adapter扩展了Adaptee,同时又实现了目标(Target)接口。
public class Adapter extends Adaptee implements Target { /** * 由于源类Adaptee没有方法sampleOperation2() * 因此适配器补充上这个方法 */ @Override public void sampleOperation2() { //写相关的代码 } }
与类的适配器模式一样,对象的适配器模式把被适配的类的API转换成为目标类的API,与类的适配器模式不同的是,对象的适配器模式不是使用继承关系连接到Adaptee类,而是使用委派关系连接到Adaptee类。
从上图可以看出,Adaptee类并没有sampleOperation2()方法,而客户端则期待这个方法。为使客户端能够使用Adaptee类,需要提供一个包装(Wrapper)类Adapter。这个包装类包装了一个Adaptee的实例,从而此包装类能够把Adaptee的API与Target类的API衔接起来。Adapter与Adaptee是委派关系,这决定了适配器模式是对象的,Adapter与Target是一种实现关系。
public interface Target { /** * 这是源类Adaptee也有的方法 */ public void sampleOperation1(); /** * 这是源类Adapteee没有的方法 */ public void sampleOperation2(); }
public class Adaptee { public void sampleOperation1(){} }
public class Adapter implements Target { private Adaptee adaptee; public Adapter(Adaptee adaptee){ this.adaptee = adaptee; } /** * 源类Adaptee有方法sampleOperation1 * 因此适配器类直接委派即可 */ public void sampleOperation1(){ this.adaptee.sampleOperation1(); } /** * 源类Adaptee没有方法sampleOperation2 * 因此由适配器类需要补充此方法 */ public void sampleOperation2(){ //写相关的代码 } }
在开发过程中,ListView的Adapter是我们最为常见的类型之一。
// 代码省略 ListView myListView = (ListView)findViewById(listview_id); // 设置适配器 myListView.setAdapter(new MyAdapter(context, myDatas)); // 适配器 public class MyAdapter extends BaseAdapter{ private LayoutInflater mInflater; List<String> mDatas ; public MyAdapter(Context context, List<String> datas){ this.mInflater = LayoutInflater.from(context); mDatas = datas ; } @Override public int getCount() { return mDatas.size(); } @Override public String getItem(int pos) { return mDatas.get(pos); } @Override public long getItemId(int pos) { return pos; } // 解析、设置、缓存convertView以及相关内容 @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; // Item View的复用 if (convertView == null) { holder = new ViewHolder(); convertView = mInflater.inflate(R.layout.my_listview_item, null); // 获取title holder.title = (TextView)convertView.findViewById(R.id.title); convertView.setTag(holder); } else { holder = (ViewHolder)convertView.getTag(); } holder.title.setText(mDatas.get(position)); return convertView; } }因为ListView需要能够显示各式各样的视图,每个人需要的显示效果各不相同,显示的数据类型、数量等也千变万化。为了能够进行统一,将ListView需要的接口抽象到Adapter对象中,这样只要用户实现了Adapter的接口,它使用的就是对象适配器模式,我们只需要将数据源委托给Adapter,Adapter按照目标接口对数据源进行处理,并且提供给ListView,这样ListView就可以按照用户设定的显示效果、数量、数据来显示特定的Item View。
在上面的例子中,我们可以将BaseAdaper看做是Target,它跟Adapter是一种继承(实现)关系,将List<String>类型的mDate看作是Adaptee,它跟Adapter是一种委托关系。因为数据源的千变万化导致对ListView的适配困难,现在通过Adapter对数据源的统一适配处理,这样我们的ListView就可以按照固定的规范访问Adapter就可以了,因为Adapter是统一的,都是实现自BaseAdapter。
适配器模式的优点
更好的复用性
系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。
更好的扩展性
在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。
适配器模式的缺点
过多的使用适配器,会让系统非常零乱,不易整体进行把握。
参考文章:
《JAVA与模式》之适配器模式
Android源码之ListView的适配器模式