Android常见设计模式四:模板模式

对于开发人员来说,设计模式有时候就是一道坎,但是设计模式又非常有用,过了这道坎,它可以让你水平提高一个档次。而在android开发中,必要的了解一些设计模式又是必须的,因为设计模式在Android源码中,可以说是无处不在。对于想系统的学习设计模式的同学,这里推荐一本书,《大话设计模式》。


Android常用设计模式系列:

面向对象的基础特征
面向对象的设计原则
单例模式
模板模式
适配器模式
工厂模式
代理模式
原型模式
策略模式
Build模式
观察者模式
装饰者模式
中介模式
门面模式


模板模式

模板模式是非常常见的设计模式之一,写个笔记,记录一下我的学习过程和心得。

首先了解一些模板方法模式的定义。

定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。

斋看定义,也是有点难理解的,但是结合我们的开发,就比较好理解了,因为每个写过代码的人都应该或多或少的用过。

结合我们平时的一些设计,一般会涉及到对某一行为或者做某一件事情的抽象及对其的实现:

抽象父类(AbstractClass):实现了模板方法,定义了算法的骨架。
具体子类(ConcreteClass):实现抽象类中的抽象方法,即不同的对象的具体实现细节。

来一个简单的例子吧,程序猿小波觉得搞开发写代码太苦逼而且找不到女票,而且考虑到之前无意间得到一本武林秘籍《thinking in 糕点》,也尝试做过几个美味可口的蛋糕。于是,他决定离职自己创业开了个蛋糕店,然后再招个漂亮的女服务员(关键是要单身,然后内部消化),这样就OK了。
小波经过日夜修炼秘籍,掌握各式糕点的做法,并且融汇贯通,还总结了一套做各式蛋糕的流程:

  1. 材料,各式蛋糕需要各种不同的配料;
  2. 器具,各式蛋糕需要各种不同的器具;
  3. 时间,各式蛋糕成型的时间不一。
public abstract class AbstractCake {
    /**
     * 具体的整个过程
     */
    public void doCake(){
        this.doWhat();
        this.burden();
        this.appliance();
        this.time();
    }
    
    /**
     * 选择做什么蛋糕
     */
    protected abstract void doWhat();

    /**
     * 备什么材料
     */
    protected abstract void burden();

    /**
     * 选用什么器具
     */
    protected abstract void appliance();

    /**
     * 做多长时间
     */
    protected abstract void time();
}

店铺马上要开张了,并且镁铝服务员也招到了,小波也做过市场调研,马上开始了做最受欢迎的几款蛋糕如下:

草莓奶油蛋糕-StrawberryCake

/**
* 小波蛋糕店版权所有,2018-09-01 21:25:25
* @author 小波
*/
public class StrawberryCake extends AbstractCake {

    @Override
    protected void doWhat() {
        System.out.println("草莓奶油蛋糕");
    }

    @Override
    protected void burden() {
        System.out.println("我去买草莓和奶油");
    }

    @Override
    protected void appliance() {
        System.out.println("只需要烘焙机");
    }

    @Override
    protected void time() {
        System.out.println("烘焙十五分钟搞定");
    }
}

栗蓉暗香-ChestnutCake

/**
* 小波蛋糕店版权所有,2018-09-01 21:25:25
* @author 小波
*/
public class ChestnutCake extends AbstractCake {

    @Override
    protected void doWhat() {
        System.out.println("栗蓉暗香蛋糕");
    }

    @Override
    protected void burden() {
        System.out.println("我去买栗子");
    }

    @Override
    protected void appliance() {
        System.out.println("需要十字小道、电饼铛和烘焙机");
    }

    @Override
    protected void time() {
        System.out.println("烘焙十六分钟搞定");
    }
}

榴莲飘飘-DurianCake

/**
* 小波蛋糕店版权所有,2018-09-01 21:25:25
* @author 小波
*/
public class DurianCake extends AbstractCake {

    @Override
    protected void doWhat() {
        System.out.println("榴莲飘飘蛋糕");
    }

    @Override
    protected void burden() {
        System.out.println("我去买榴莲");
    }

    @Override
    protected void appliance() {
        System.out.println("需要刀、烘焙机");
    }

    @Override
    protected void time() {
        System.out.println("烘焙十二分钟搞定");
    }
}

开始做蛋糕

/**
* 小波蛋糕店版权所有,2018-09-01 21:25:25
* @author 小波
*/
public class App { 
    public static void main(String[] args) {
        AbstractCake strawberry = new StrawberryCake();
        strawberry.doCake();

        System.out.println("-----------------------------");

        AbstractCake chestnut = new ChestnutCake();
        chestnut.doCake();

        System.out.println("-----------------------------");

        AbstractCake durian = new DurianCake();
        durian.doCake();

    }
}

输出如下:

草莓奶油蛋糕
我去买草莓和奶油
只需要烘焙机
烘焙十五分钟搞定
-----------------------------
栗蓉暗香蛋糕
我去买草莓和奶油
需要十字小道、电饼铛和烘焙机
烘焙十六分钟搞定
-----------------------------
榴莲飘飘蛋糕
我去买榴莲
需要刀、烘焙机
烘焙十二分钟搞定

这样我们就实现了使用模板模式的一个完整的实例。

广泛应用

在Android的源码中,模板模式的使用可以说无处不在

1 系统启动过程
2.组件生命周期,比如Activity和Service等
3.一些具体封装类,比如AsyncTask等

Activity和Service是每个接触过Android学习的人都会非常的了解,这里不做详细的分析。我们主要分析一下常用的AsyncTask。
首先众所周知,AsyncTask的模板就是那几个抽象方法,等你去实现,我们每次使用这个类,就是实现一个具体的子类,而且一个对象只能用一次。

1.onPreExecute() 
2.doInBackground() 
3.onProgressUpdate() 
4.onPostExecute()

抽象父类的细节
首先介绍几个角色:
1.ThreadPool:AsyncTask默认用线程池来切换线程,这个线程池在不同Android版本是不一样的,最初始串行的,后来是并行,现在又是串行,是一个全局的线程池。
2.Handler:这个用于线程的消息交互,主要是子线程通知到UI线程,因为onPostEx’ecute()是在UI线程,所以我们handler必须在U线程初始化。

为什么AsyncTask必须在UI线程初始化呢?
就是以为内部的Handler必须是绑定UI线程的,而这个handler绑定的线程是也是在AsyncTask初始化的当前线程。

介绍模板调用细节

1.首先执行AsyncTask.Execute(Params)
这个方法会调用ExecuteOnExecutor(Executor,Params),会传入默认的线程池:sDefaultExecutor和参数

2.ExecuteOnExecutor(Executor,Params)
这个方法会做以下几件事:

1.开始判断AsyncTask对象是否处于Running和Finish状态,如果是就会弹出异常。然后设定这个AsyncTask对象处于running状态。这也是为什么一个AsyncTask对象只能用一次。
2.调用onPreExecute()做一些初始化和准备。
3.调用doInBackground,初始化mWorker,并把Params赋值给mWorker的mParams成员。mWorker实现了Callable接口并在视线中调用了postResult(doInBackground(Params)),这个方法是关键,完成了线程切换,我们后面展开讲。
4.初始化mFuture,这是一个FutureTask对象,可以理解为一个Thread对象。
5.sDefaultExecutor.execute(mFuture),mFuture的run()方法会调用mWorker的call回调方法,最终调用postResult(doInBackground(Params))

  1. postResult(doInBackground(Params))方法
    以上是onPreExecute()和doInBackground()方法已经被调用了
    postResult(doInBackground(Params))这个方法做了一下几个事。

1.构造一个AsyncTaskResult,这个对象是两个对象构造:
AsyncTask对象实例
doInBackground执行完的Result
2.构造一个UI线程Handler的Message,what是MESSAGE_POST_RESULT,obj是AsyncTaskResult
3.发送给UI线程Handler处理。这个handler能处理两类消息就是:
MESSAGE_POST_RESULT:收到这个消息后切换到UI线程,取出obj里面的AsyncTask对象实例调用finish()方法,finish方法会调用>onPostExecute()方法,并且把状态标为finish。
MESSAGE_POST_PROGRESS消息:
会调用AsyncTask对象实例的onUpdateProgress()方法

以上就是AsyncTask的模板流程,我们只需要实现流程中一些对我们开放的特有的细节(就是前文提到的几个接口)就OK,无法影响整个流程的结构。

总结

其实,模板模式说得通俗点一点就是 :完成一件事情,有固定的数个步骤,但是每个步骤根据对象的不同,而实现细节不同;就可以在父类中定义一个完成该事情的总方法,按照完成事件需要的步骤去调用其每个步骤的实现方法。每个步骤的具体实现,由子类完成。
接下来,总结一下模板模式的优缺点

优点

  1. 具体细节步骤实现定义在子类中,子类定义详细处理算法是不会改变算法整体结构。
  2. 代码复用的基本技术,在数据库设计中尤为重要。
  3. 存在一种反向的控制结构,通过一个父类调用其子类的操作,通过子类对父类进行扩展增加新的行为,符合“开闭原则”。

缺点

每个不同的实现都需要定义一个子类,会导致类的个数增加,系统更加庞大。

适用场景

  1. 在多个子类中拥有相同的方法,而且逻辑相同时,可以将这些方法抽出来放到一个模板抽象类中。
  2. 程序主框架相同,细节不同的情况下,也可以使用模板方法。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容

  • 一、简历准备 1、个人技能 (1)自定义控件、UI设计、常用动画特效 自定义控件 ①为什么要自定义控件? Andr...
    lucas777阅读 5,129评论 2 54
  • 1 概述 在操作系统中,线程是操作系统调度的最小单元,同时线程又是一种受限的系统资源,即线程不可能无限制地...
    Kevin_Junbaozi阅读 633评论 0 2
  • 第5章 多线程编程 5.1 线程基础 5.1.1 如何创建线程 在java要创建线程,一般有==两种方式==:1)...
    AndroidMaster阅读 1,769评论 0 12
  • 参考 https://www.baidu.com/link?url=QNRznJEBT25k0bpgVD3bOni...
    CodeInfo阅读 365评论 0 0
  • 我们经常听到一句俗语叫做要给学生一杯水,教师必须有一桶水。如果就针对小学数学的内容来说,大家会觉得太简单了...
    偷闲躲静阅读 248评论 0 0