模板方法

模板方法就是预先定义好一个操作中的算法的框架,然后将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

首先,我们以常见到我们可能都没注意到 模板方法开始。在我们开发安卓程序中,必然少不了Activity和Fragment的使用(还有Service等构件)。我们往往会继承一个Activity来实现自己的Activity逻辑,常见的代码快就是在Activity的onCreate/onStart/onResume等生命周期内完成对应的操作:

public class BaseActivity extends Activity{
  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
  }
  @Override
  protected void onResume() {
      super.onResume();
  }
  @Override
  protected void onPause() {
      super.onPause();
  }
  @Override
  protected void onStop() {
      super.onStop();
  }
  @Override
  protected void onDestroy() {
      super.onDestroy();
  }

}

模板方法涉及到一个好莱坞原则--“不要给我打电话,我会主动和你打电话”,在模板方法中扮演好莱坞的角色是抽象类,子类是演员的角色。一般需要调用子类中的方法都已经在模板中定义好了,需要时,会主动调用子类的各个步骤。Activity正是根据预设好的Activity调用生命周期的流程,程式化的执行我们继承的Activity,这就是Activity的模板方法。实际上,我们开发安卓程序,大部分的时间都是在覆写制定的方法。

再举个列子就是我们在需要进行一些耗时的操作的时候,经常会使用AsyncTask。AsyncTask中首先会执行execute方法,而execute方法内部封装了onPreExecute, doInBackground, onPostExecute这个算法框架,用户可以根据自己的需求来在覆写这几个方法。例如在doInBackground中完成耗时操作(文件读取、网络请求),最后在onPostExecute中进行UI刷新的操作。

观察者模式

有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

观察者模式常见的使用场景就是模型-视图(View)模式,当模型(数据)变化的时候,对应的UI就应该进行更新。这里我们以安卓的ListView作为列子来说明观察者模式。

开发者在使用ListView的时候,一般都会继承一个BaseAdapter,然后通过setAdapter的方式将ListView和BaseAdapter类绑定,当程序知道数据变化的时候,调用BaseAdapter的notifyDataSetChanged()就会更新ListView的列表信息。

我们首先看下BaseAdapter中的部分代码:

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
  //被观察者
  private final DataSetObservable mDataSetObservable = new DataSetObservable();
  //注册观察者
  public void registerDataSetObserver(DataSetObserver observer) {
      mDataSetObservable.registerObserver(observer);
  }
  //删除观察者
  public void unregisterDataSetObserver(DataSetObserver observer) {
      mDataSetObservable.unregisterObserver(observer);
  }

  /**
   通知观察者数据发生了变化
   * /
  public void notifyDataSetChanged() {
      mDataSetObservable.notifyChanged();
  }

  //其他
}

很明显BaseAdapter中正是使用了观察者模式。我们继续看一下mDataSetObservable.notifyChanged()中的notifyChanged方法。

public void notifyChanged() {
      synchronized(mObservers) {
          // since onChanged() is implemented by the app, it could do anything, including
          // removing itself from {@link mObservers} - and that could cause problems if
          // an iterator is used on the ArrayList {@link mObservers}.
          // to avoid such problems, just march thru the list in the reverse order.
          for (int i = mObservers.size() - 1; i >= 0; i--) {
              mObservers.get(i).onChanged();
          }
      }
}

可以看到在notifyChanged方法中,就是遍历所有的观察者,然后调用onChanged方法。下面我们在看一下这些观察者是在什么时候被加入到观察者列表中的,因为我们就是通过ListView的setAdapter方法来绑定BaseAdapter对象(可以看做是数据源)的,我们直接定位到setAdapter方法。

public void setAdapter(ListAdapter adapter) {
  //首先如果这个ListView已经有Adapter了,就先删除该Adapter对应的观察者
    if (mAdapter != null && mDataSetObserver != null) {
        mAdapter.unregisterDataSetObserver(mDataSetObserver);
    }

    ...

    super.setAdapter(adapter);

    if (mAdapter != null) {
        ...
        //这里创建了一个观察者,然后注册到Adapter的观察者列表中
        mDataSetObserver = new AdapterDataSetObserver();
        mAdapter.registerDataSetObserver(mDataSetObserver);
        ...
    } else {
      ...
    }

    requestLayout();
}

下面我们在看看ListView的抽象类AbsListView中定义的AdapterDataSetObserver观察者。

class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
    @Override
    public void onChanged() {
        super.onChanged();
        ...
    }

}

又继承自AbsListView的父类AdapterView中定义的AdapterDataSetObserver

class AdapterDataSetObserver extends DataSetObserver {

    ...
    @Override
    public void onChanged() {
        ...
        //这里我们就只要关注onChanged方法中会调用requestLayout方法进行重新布局
        requestLayout();
    }
    ...
}

这里我们就看到了我们一开始在BaseAdapter中调用notifyDataSetChanged时调用的DataSetObserver对象调用的onChanged方法,引起UI的重新布局和绘制。

组合模式

组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。掌握组合模式的重点是要理解清楚 “部分/整体” 还有 ”单个对象“ 与 "组合对象" 的含义。

说到组合模式,我们很自然就想到安卓中的UI控件,以及View和ViewGroup类。我们先简单看一下安卓中UI控件的继承关系,Android中所有控件都继承自android.view.View,其中android.view.ViewGroup是View的一个重要子类,绝大部分的布局都继承自ViewGroup。下面是安卓控件的继承关系类图,其中红色为常用控件:

安卓UI控件继承关系

我们先看一下组合模式的通用类图:

组合模式的通用类图

我们可以认为认为View就是Component,而TextView这类普通控件就是Leaf,而LinearLayout之类的布局就是Composite的具体实现。事实上ViewGroup中就有addView/removeView/getChild等方法可以对View控件进行操作。我们知道,UI界面要展现出来,需要计算、布局、渲染三个过程,其实就对应了View中的onMeasure、onLayout和onDraw三个方法(哇塞,这里不就是一个模板方法吗!)。这里,我们就看一下View中的onMeasure方法,即使计算View应该布局的位置(坐标、长宽)。

对于普通的View对象,例如TextView,就根据设置好的layout_width和layout_height等属性,直接计算。但是对于ViewGroup来说,因为ViewGroup可以嵌套任意的View,我们来看看ViewGroup的onMeasure过程,在ViewGroup这个抽象类中并没有实现onMeasure,因为对不不同类型的ViewGroup(布局),它的计算方法肯定是不同的,我们直接看LinearLayout(线性布局)的代码:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (mOrientation == VERTICAL) {
        measureVertical(widthMeasureSpec, heightMeasureSpec);
    } else {
        measureHorizontal(widthMeasureSpec, heightMeasureSpec);
    }
}

由于可能有横向的线性布局,也可能有纵向的线性布局,这里我们就直接看纵向的measureVertical(widthMeasureSpec, heightMeasureSpec)方法,这里代码比较长,我们就看其中的一些关键代码:

    //获取子UI构件的数目
    final int count = getVirtualChildCount();

    //根据设置的layout_width、layout_height是match_parent还是wrap_content会有不同的计算方法,这里我们不深究
    final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    final int heightMode = MeasureSpec.getMode(heightMeasureSpec);

    ...

    // See how tall everyone is. Also remember max width.
    for (int i = 0; i < count; ++i) {
        final View child = getVirtualChildAt(i);

        ...
        LinearLayout.LayoutP
        arams lp = (LinearLayout.LayoutParams) child.getLayoutParams();

        totalWeight += lp.weight;

        ...
    }

在上述的方法中,我们可以看到会遍历所有子UI构件,然后递归调用子UI构件的measure方法最后计算得到该ViewGroup的高度和宽度。这个方法用来测量出view的大小。父view使用width参数和height参数来提供constraint信息。实际上,view的测量工作在onMeasure(int,int)方法中完成。因此,只有onMeasure(int,int)方法可以且必须被重写。参数widthMeasureSpec提供view的水平空间的规格说明,参数heightMeasureSpec提供view的垂直空间的规格说明。通过这种方式我们就能够很方便的进行计算、布局、渲染的过程了。

参考资料

http://www.cnblogs.com/fengzhblog/archive/2013/05/13/3075627.html

https://github.com/simple-android-framework-exchange/android_design_patterns_analysis

http://haolloyin.blog.51cto.com/1177454/347308/

登录发表评论 注册

David

总结得很不错,其他模式有时间可以继续谢谢,还有很多模式可以挖掘smile

反馈意见