Android源码中的模板方法模式

从装饰者模式到Context类族

当观察者模式和回调机制遇上Android源码

Android源码中的静态工厂方法

Android中的工厂方法模式

Android源码中的命令模式

Android源码中的适配器模式

Android源码中的外观模式

定义

模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

使用场景

  1. 对一些复杂的算法进行分割,将其算法中固定不变的部分设计为模板方法和父类具体方法,而一些可以改变的细节由其子类来实现。即:一次性实现一个算法的不变部分,并将可变的行为留给子类来实现。

  2. 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。

  3. 需要通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制。

结构

模式所涉及的角色有:

  • 抽象类(AbstractClass):实现了模板方法,定义了算法的骨架。

  • 具体类(ConcreteClass):实现抽象类中的抽象方法,已完成完整的算法。

模板方法中的方法可以分为两大类:模板方法和基本方法。

  • 模板方法

    一个模板方法是定义在抽象类中的,把基本操作方法组合在一起形成一个总算法或一个总行为的方法。

    一个抽象类可以有任意多个模板方法,而不限于一个。每一个模板方法都可以调用任意多个具体方法。

  • 基本方法

    基本方法又可以分为三种:抽象方法(Abstract Method)、具体方法(Concrete Method)和钩子方法(Hook Method)。

    • 抽象方法:一个抽象方法由抽象类声明,由具体子类实现。在Java语言里抽象方法以abstract关键字标示。

    • 具体方法:一个具体方法由抽象类声明并实现,而子类并不实现或置换。

    • 钩子方法:一个钩子方法由抽象类声明并实现,而子类会加以扩展。通常抽象类给出的实现是一个空实现,作为方法的默认实现。

实现

分析完理论,我们用一个饮料店出售饮料的小例子来分析一下模板方法模式。夏天我们去饮料店购买饮料,告诉服务员要一大杯奶茶,还要加冰

那么饮料店是如何装配我们的饮料的呢?首先准备一个大杯子,然后入我们需要的饮料,如果加冰会放入一些冰块,最后盖好盖子打包给我们。

仔细思考一下我们的使用场景,有没有发现很符合呢?接下来我们用代码实现一下。

抽象方法,装配饮料的模板

/**
 * 饮料模板
 * Created by lei on 2016/10/19.
 */

public abstract class TemplateDrink {

    /**
     * 获取饮料的方法,用final修饰,防止被子类修改算法结构。模板方法
     */
    public final void getDrink() {
        getGlass();
        getContent();
        if (hook()) {
            Log.e(TAG, "加冰--->");
        } else {
            Log.e(TAG, "不加冰--->");
        }
        pack();
    }

    /**
     * 获取杯子,杯子分大中小杯
     */
    public abstract void getGlass();

    /**
     * 获取具体种类饮料
     */
    public abstract void getContent();

    /**
     * 夏天我们的饮料是默认加冰的。钩子方法
     */
    public boolean hook() {
        return true;
    }

    /**
     * 打包
     */
    public void pack() {
        Log.e(TAG, "包装");
    }
}

这里需要注意的是getDrink()方法是用final修饰的,这样就保证了逻辑流程不会被子类修改,子类只能修改某一个步骤的具体实现。

具体某种饮料的实现

/**
 * 具体的饮料,比如奶茶
 * Created by lei on 2016/10/19.
 */

public class ConcreteDrink extends TemplateDrink {

    @Override
    public void getGlass() {
        Log.e(TAG, "中杯--->");
    }

    @Override
    public void getContent() {
        Log.e(TAG, "奶茶--->");
    }

    @Override
    public boolean hook() {
        return super.hook();
    }
}

Android源码中的模板方法模式

这个模式相对来说比较简单,但是应用却非常广泛,毕竟越简单越容易应用嘛。比如在重构的时候,把相同的代码抽取到父类中,然后配合钩子函数约束其行为。

当然在Android源码中的应用肯定也不少,比如AsyncTask的实现,调用execute后会依次执行onPreExecute、doInBackground、onPostExecute,还可以通过onProgressUpdate来更新进度,它的执行过程其实是一个框架,类似的还有Activity的生命周期回调方法的执行。

接下来我们看一个比较明显的,即View中的Draw()方法

我们先看下方法注释

/**
* Manually render this view (and all of its children) to the given Canvas.
* The view must have already done a full layout before this function is
* called. When implementing a view, implement
* {@link #onDraw(android.graphics.Canvas)} instead of overriding this method.
* If you do need to override this method, call the superclass version.
*
* @param canvas The Canvas to which the View is rendered.
*/

在我们调用layout()方法之后,可以调用draw()方法来手动渲染view(包括它的子view)到传入的canvas上。如果我们继承View类的话需要重写ondraw()方法而不是draw()。如果你必须重写,需要调用超类的版本。

draw()方法比较长,我们挑主要的看一下。

方法开头有一段注释是这样的:

/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas’ layers to prepare for fading
* 3. Draw view’s content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/

遍历执行几个绘制步骤,并且这些步骤需要按适当顺序执行。

我们重点看下3和4

// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);

// Step 4, draw the children
dispatchDraw(canvas);

具体方法如下

/**
 * Implement this to do your drawing.
 *
 * @param canvas the canvas on which the background will be drawn
 */
protected void onDraw(Canvas canvas) {
}

/**
 * Called by draw to draw the child views. This may be overridden
 * by derived classes to gain control just before its children are drawn
 * (but after its own view has been drawn).
 * @param canvas the canvas on which to draw the view
 */
protected void dispatchDraw(Canvas canvas) {

}

我们看一下View类族的UML图

这里写图片描述

子类只能通过扩展onDraw(Canvas canvas)和dispatchDraw(Canvas canvas)两个函数,使子类的显示效果和别的具体子类。

接下来对号入座,draw()对应我们的模板方法,ondraw()和dispatchDraw()就是钩子方法。

搞定,收工。

测试代码已上传到github

Android应用源码45套安卓源码合集: android文离线发音引擎FOCTTS使用源码.rar Android应用源码(精)LBS签到应用源码.rar Android应用源码(精)xUtils2.2.5.rar Android应用源码(精)仿博客园客户端源码.rar Android应用源码(精)手机控制电脑鼠标.rar Android应用源码(精)记事本小程序,加注释,适合阅读.rar Android应用源码Android平台下通过HTTP协议实现断点续传下载.rar Android应用源码Hibernate4Android.rar Android应用源码http、udp、tcp网络交互组件.rar Android应用源码ListView实现的目录树结构.rar Android应用源码SdCard读写文件实例.rar Android应用源码SlidingMenu使用例子.rar Android应用源码串口通信(JNI)例子.rar Android应用源码任务提醒源码.rar Android应用源码仿360手机助手首页浮动菜单.rar Android应用源码仿Iphone抖动效果Shake Icon.rar Android应用源码仿QQ分组列表修改版.rar Android应用源码使用listView实现的树状结构.rar Android应用源码俄罗斯方块注释超详细版.rar Android应用源码利用poi将内容填到word模板.rar Android应用源码动态列表布局.rar Android应用源码单Java文件实现的计算器.rar Android应用源码基于百度云推送的聊天工具源码.rar Android应用源码安卓多边形布局例子.rar Android应用源码安卓拍照上传实现代码附带php端.rar Android应用源码实现动态交叉布局.rar Android应用源码小说翻页效果源码.rar Android应用源码广告轮播效果源码.rar Android应用源码强大的统计图表库.rar Android应用源码微享,微信分享实例.rar Android应用源码有米广告SDK例子.rar Android应用源码模仿zaker风景页面滑动效果修改版.rar Android应用源码水波纹动画效果.rar Android应用源码泡泡效果bubble.rar Android应用源码猜猜红桃A.rar Android应用源码百度统计例子.rar Android应用源码简单的Android图片轮播.rar Android应用源码简单的仿微信实现了表情效果.rar Android应用源码结合数据库进行摇一摇的实例.rar Android应用源码花姑娘之部分UI源码.rar Android应用源码获取手机信息.rar Android应用源码讯飞语音测试源码.rar Android应用源码飞碟说欢迎界面.rar
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值