案例分析:设计模式与代码的结构特性
一、设计模式
设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。它与架构的区别在于设计模式比架构更抽象,是比架构更小的体系结构元素,是从代码层面总结的实现一个模块最优的方法。而常见的设计模式按照范围可以分为类模式和对象模式,按照目的可以分为创建型模式、结构型模式、行为型模式。创建型模式是对象实例化的模式,创建型模式用于解耦对象的实例化过程,包括抽象工厂模式、生成器模式、工厂模式、原型模式、单件模式;结构型模式是把类或对象结合在一起形成一个更大的结构,包括适配器模式(类对象)、组合模式(对象)、装饰模式(对象);行为型模式是描述类和对象如何交互,及划分责任和算法,包括迭代器模式、观察者模式、状态模式。在本篇文章中将采用结构模式中的适配器模式。
二、适配器模式(Adapter)
结构型模式涉及到如何组合类和对象以获得更大的结构,它的类模式采用继承机制来组合接口或者实现。其中的适配器模式是将一个类的接口转化为客户希望的另外一个接口,Adapter模式使得原本由于接口不兼容而不能够在一起工作的类可以一起工作。在对象适配器模式中,适配器与适配者之间是关联关系;在类适配器模式中,适配器与适配者之间是继承(或实现)关系。
首先已存在一个类和一个接口:
public class Adaptee { public void adapteeRequest() { System.out.println("被适配者的方法"); } } public interface Target { void request(); }
为了在目标接口中的 request()
调用 Adaptee
的 adapteeRequest()
方法,直接定义一个类使用Target接口直接调用是行不通的,此时就需要一个适配器类来实现 Target
接口。
对于类适配器而言:
它通过一个适配器类,实现 Target
接口,同时继承了 Adaptee
类,然后在实现的 request()
方法中调用父类的 adapteeRequest()
public class Adapter extends Adaptee implements Target{ @Override public void request() { //...... super.adapteeRequest(); //...... } }
而对于对象适配器:
它是通过关联实现的:
public class Adapter implements Target{ // 适配者是对象适配器的一个属性 private Adaptee adaptee = new Adaptee(); @Override public void request() { //... adaptee.adapteeRequest(); //... } }
三、实例应用
在前文中简单介绍了适配器模式的基本概念,在这一部分通过一段实例代码来展示适配器模式在实际中的应用。
在安卓开发中,适配器模式的应用十分广泛,Adapter到处可见,例如我们经常用到的ListView空间,对它的一些使用就需要用到适配器,以下是一个app源码中ListAdapter应用:
ListAdapter接口如下:
public interface ListAdapter { public int getCount(); Object getItem(int position); long getItemId(int position); View getView(int position, View convertView, ViewGroup parent); boolean isEmpty(); }
抽象类BaseAdapter,只列出了两个方法:
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter { // ... ... public View getDropDownView(int position, View convertView, ViewGroup parent) { return getView(position, convertView, parent); } public boolean isEmpty() { return getCount() == 0; } }
然后再定义一个类ArrayAdapter对List<T>进行封装成ListAdapter的实现,满足ListView的调用:
public class ArrayAdapter<T> extends BaseAdapter implements Filterable { private List<T> mObjects; //构造函数,只列出了一个 public ArrayAdapter(Context context, int textViewResourceId, T[] objects) { init(context, textViewResourceId, 0, Arrays.asList(objects)); } private void init(Context context, int resource, int textViewResourceId, List<T> objects) { mContext = context; mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mResource = mDropDownResource = resource; mObjects = objects; //引用对象 mFieldId = textViewResourceId; } public int getCount() { return mObjects.size(); } public T getItem(int position) { return mObjects.get(position); } public View getView(int position, View convertView, ViewGroup parent) { return createViewFromResource(position, convertView, parent, mResource); } // ... ... }
通过这种方法,把List<T>作为数据源以ListView想要的目标接口的样子传给了ListView。
四、分析
在安卓开发中我们用到Adapter通常是以下几种情况:
1. 你想使用一个已经存在的类,而它的接口不符合你的需求,这个在处理旧系统时比较常见。
2. 你想创建一个可以复用的类,该类可以和其他不相关的类或不可预见的累协同工作,这就是我们android开发者经常碰到的情况:我们常常自定义一个新的Adapter。
3. 你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配他们的接口,对象适配器可以适配他的父类接口。
在其他场景下的应用也是一样的,引入了适配器之后,它带来了以下这些好处:
-
将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。
-
增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用。
-
灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”
这种模式下的多态机制就是通过适配器实现的,它通过继承或是关联的方式来完成适配。
对于模块封装的方法,调用者封装成一个类,被调用的适配类是一个类,适配器封装成为一个类。适配器模块内聚度低,但与其他接口的耦合度高。