Android中的设计模式-适配器模式

适配器模式

从名字上就可以看出适配器是为了针对接口不匹配的情况,而做出的兼容方法,
假设我们有一个已经存在的类Adaptee,其中有一些已经存在并实现好的方法methodA。但是客户不希望调用这个类,而是希望调用一个特定的接口例如Target接口。
于是如果想要调用Adaptee.methodA()方法,创建一个Adapter类实现客户要求的Target接口,Target接口中假设客户希望调用callMethodA()方法来实现Adaptee.methodA()方法功能。能够想到的就是以下两种方式:

类的适配器模式

让Adapter类实现Target接口接口同时继承Adaptee类,这样Adapter类就继承了Adaptee.methodA(),在其callMethodA()中调用父类的methodA()方法即可。客户新建Adapter类对象,就可以通过Target接口调用Adapter.methodA()。

类图

技术分享

代码实现

等待被适配的Adaptee

public class Adaptee {
    public void methodA(){
        System.out.println("I am the methodA in Adaptee");
    }
}

客户期待的Target接口

public interface Target {
    public void callMethodA();
    public void otherMethod();
}

继承了Adaptee并实现Target接口的适配者

public class Adapter extends Adaptee implements Target {
    @Override
    public void callMethodA() {
        super.methodA();
    }
    @Override
    public void otherMethod() {
        System.out.println("I am the otherMethod in Adapter");
    }
}

客户类测试

public class Client {

    public static void main(String[] args) {
        Adapter mAdapter = new Adapter();
        mAdapter.callMethodA();
        mAdapter.otherMethod();
    }

}

输出结果

I am the methodA in Adaptee
I am the otherMethod in Adapter

对象的适配器模式

让Adapter类中持有一个Adaptee类的实例,在Adapter类实现Target接口的methodA()方法时,在其中调用Adaptee实例的methodA()方法即可。

类图

技术分享

代码实现

在上面的类的适配器基础上,只需要修改Adapter和客户类即可。

继承了Adaptee并实现Target接口的适配者


public class Adapter implements Target {
    private Adaptee mAdaptee;

    public Adapter(Adaptee mAdaptee) {
        this.mAdaptee = mAdaptee;
    }
    @Override
    public void callMethodA() {
        this.mAdaptee.methodA();
    }
    @Override
    public void otherMethod() {
        System.out.println("I am the otherMethod in Adapter");
    }
}

客户类测试

public class Client {

    public static void main(String[] args) {
        Adaptee mAdaptee=new Adaptee();
        Adapter mAdapter = new Adapter(mAdaptee);
        mAdapter.callMethodA();
        mAdapter.otherMethod();
    }

}

输出结果也是一样的

输出结果

I am the methodA in Adaptee
I am the otherMethod in Adapter

两种适配器的对比

两种模式各有利弊,类的适配器模式已经继承了Adaptee类,就没法再继承其他的类,这时它的弊端,不过好处就是逻辑比较清晰,适合只适配单一的Adaptee的情况。对象的适配器模式则没有继承的限制,采用聚合的方式来适配待适配的类。个人个倾向于对象的适配器模式这种适配器方式,耦合性更低。

缺省适配器模式

适配器还有第三种模式,看起来和上面的两种适配器模式不太一样,
对于一个定义了较多方法的接口,我们实现该接口的时候,一来实现这么多的方法很麻烦。二来有些方法我们并不需要,都实现了反而混淆了重点。
为了解决这个问题,就是用一个抽象类继承该接口,并在抽象类中使用空实现来实现接口中的方法。(也不一定,有些情况抽象类中会有接口中的方法的默认实现,而继承它的子类中只需要完成其特有的一些方法,或者在原有方法的基础上进行增加。例如Android中的PhoneBase类,BaseAdapter类等)。

类图

代码实现

public interface Target {
    public void A();
    public void B();
    public void C();
    public void D();
    public void E();
    public void F();
}

public abstract class AbstractTarget implements Target {

    @Override
    public void A() {
    }
    //可以留一个B方法不实现,B还是抽象的,留给子类必须实现
    @Override
    public void C() {
        System.out.println("I am C in AbstractTarget");
    }

    @Override
    public void D() {
    }

    @Override
    public void E() {
    }

    @Override
    public void F() {

    }

}


public class ConcreteTarget extends AbstractTarget {

    @Override
    public void B() {//B是必须实现的
        System.out.println("I am B in ConcreteTarget");
    }

    @Override
    public void A() {//选择性的重写A和C方法
        System.out.println("I am A in ConcreteTarget");
    }

    @Override
    public void C() {
        super.C();
        System.out.println("I am C in ConcreteTarget");
    }


}

测试

    public static void main(String[] args) {
        Target mTarget = new ConcreteTarget();
        mTarget.A();
        mTarget.B();
        mTarget.C();
    }

Android中的适配器模式

Android中最常见的适配器模式就属于Android中的Adapter了,其应用融合了缺省适配器模式和对象的适配器模式
抽象类BaseAdapter实现ListAdapter和SpinnerAdapter两个接口,而这两个接口都是继承自Adapter接口,这两个接口分别又定义了自己的方法

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter 

不过BaseAdapter中并未实现接口中的全部方法
SimpleAdapter则继承了BaseAdapter,并实现了所有没实现的接口方法,有些BaseAdapter已经实现的方法,并没有实现。

public class SimpleAdapter extends BaseAdapter

这里就是上面的缺省适配器模式。

那么对象的适配器呢,
当我们使用SimpleAdapter时,例如:

ListView.setAdapter(new SimpleAdapter(this, getData(path),
                android.R.layout.simple_list_item_1, new String[] { "title" },
                new int[] { android.R.id.text1 }));

先看一下SimpleAdapter构造函数

    public SimpleAdapter(Context context, List<? extends Map<String, ?>> data,
            int resource, String[] from, int[] to)

其中第2个参数是一个Map的队列,对应每一个List的Item的数据,第3个参数则是展开这个Item的View的layout资源的整形ID。第4个参数from数组是第2个参数的Map的key,最后一个参数to数组则是from参数的每一个key对应的数据放置在哪个组件里。

在ListView调用onMeasure确定组件尺寸时,就会调用到mAdapter.getCount(),调用这个SimpleAdapter的getCount()函数:

    public int getCount() {
        return mData.size();
    }
    public Object getItem(int position) {
        return mData.get(position);
    }

间接调用之前创建SimpleAdapter时构造函数传递的data数据。
在绘制的时候,则会调用

mAdapter.getView(position, null, this)

获取适配器所适配的数据所对应的View来进行绘制。

所以这里SimpleAdapter就是对data对象的对象适配器模式,如果不使用适配器,ListView直接持有data数据当然也可以得到数据。但是ListView使用Adapter的精髓就是不需要去管数据是什么,只需要创建不同的Adapter,就可以做出不同效果,含有不同组件的ListView。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。