通过Android View的两种事件响应方法比较inheritance和composition

Android view有两种主要的处理事件的方式,

  1. 在View的子类中覆盖onXXX方法。因为这是在子类中通过覆盖的方式来响应事件,我称之为基于继承(inheritance)的响应方式。
  2. 调用View.setXXXListener,参数会实现View.OnXXXListener接口。因为View对象和Listner组合起来完成工作,我称之为基于组合(composition)的响应方式。

例如对于touch事件而言,View.dispatchTouchEvent接收到touch事件对象,然后:

  1. 调用通过View.setOnTouchListener注册的listener。
  2. 调用可以被子类覆盖的onTouchEvent方法。

举个简单例子,一个Activity中有一个edit框(EditText对象)。EditText是通过代码添加的到Activity中的。我希望在点击它时,自动把该框的底色设置为红色。

如果采用第一种方式,其EditText的子类是:

public class TouchChangeBackgroundColorEditText extends EditText {

    public MyEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        this.setBackgroundColor(Color.RED);
        return super.onTouchEvent(event);
    }
}

然后我们在Activity中使用该EditText的子类。

如果采用第二种方法,则需要创建一个对touch事件的listener:

public class TouchChangeBackgroundColorListener implements View.OnTouchListener {

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        v.setBackgroundColor(Color.RED);
        return false;
    }
}

然后在Activity中使用普通EditText,并对该EditText调用setOnTouchListener(new TouchChangeBackgroundColorListener)。

然后用户又想对某些EditText使用Key event来改变背景颜色,那如果采用基于继承的方式,用户需要创建KeyChangeBackGroundColorEditText的子类,在其中覆盖onKeyDown方法;如果采用组合的方式,则需要实现OnKeyListener并添加到EditText中。

现在问题来了,用户希望某些EditText是不响应touch和key事件的,有些只响应一种,有些两种都响应。那么如果采用继承的方式,那需要四种对象,一个基本都EditText和三种针对only touch,only key和both touch and key的子类。

但如果采用组合的方式,我们仍然只需要两个listener,只需对不同的EditText添加不同的listener即可。

如果更复杂,我们还想针对trackball改变颜色,那如果采用继承,则可能有3×3=9种类。但采用组合,只需要三种listener,然后EditText根据需要添加即可。

所以说,在可能有多种因素导致变化的时候,继承可能导致对象种类(即类)爆炸式增长;而组合可以用不同的对象封装不同的变化,减少类的个数。

但这里减少的只是编码中类的个数,而在运行时如果采用继承,那对象的个数是EditText的个数;但如果采用组合,那每个EditText都对应着一个Listener,所以有更多的对象个数。所以说,组合一般较继承产生更多的运行时对象,这需要更多的内存(对象存储)和运算时间(对象间相互调用)。

现在假设app的界面上已经有一个EditText控件,我们又想在app中通过选项来控制该EditText的行为,例如我们有三个选项,分别对应touch,key和trackball事件发生时是否改变该EditText的背景颜色。那么如果采用组合,可以先创建好三个listener,然后根据选择添加listener到现有的的EditText中。但如果使用继承,根本是无法动态改变其行为了。所以说,组合可以动态改变对象的行为,而继承只能在静态改变。

再回到前面继承的onTouchEvent方法,在最后一行,我需要调用super.onTouchEvent。事实上,我开始实现时忘记调用这行了,结果点击edit框时,可以改变颜色,但无法切出输入法。在现实中覆盖父类的方法时,有的父类方法是需要在子类中被调用的,但有的却不需要。覆盖的时候要详细查看父类文档和代码。但如果采用组合的方式,我只需要关系listener自己的视线即可,并不需要调用view中跟touch处理相关的代码。所以说,继承是一种白盒复用,在覆盖父类的方法时,需要关心父类方法的实现;但组合是黑盒测试,我只需要实现接口即可,而不需要关心该接口如何被调用。

故而Design pattern中所有模式的两大基石之一就是:Favor object composition over class inheritance。

你可能感兴趣的:(inheritance)