Android的设计模式-模板方法模式

前言

Android的设计模式系列文章介绍,欢迎关注,持续更新中:

Android的设计模式-设计模式的六大原则
一句话总结23种设计模式则
创建型模式:
Android的设计模式-单例模式
Android的设计模式-建造者模式
Android的设计模式-工厂方法模式
Android的设计模式-简单工厂模式
Android的设计模式-抽象工厂模式
Android的设计模式-原型模式
行为型模式:
Android的设计模式-策略模式
Android的设计模式-状态模式
Android的设计模式-责任链模式
Android的设计模式-观察者模式
Android的设计模式-模板方法模式
Android的设计模式-迭代器模式
Android的设计模式-备忘录模式
Android的设计模式-访问者模式
Android的设计模式-中介者模式
Android的设计模式-解释器模式
Android的设计模式-命令模式
结构型模式:
Android的设计模式-代理模式
Android的设计模式-组合模式
Android的设计模式-适配器模式
Android的设计模式-装饰者模式
Android的设计模式-享元模式
Android的设计模式-外观模式
Android的设计模式-桥接模式

1.定义

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

2.介绍

  • 模板方法模式属于行为型模式。
  • 模板方法模式主要是用来定义一套流程下来的固定步骤,而具体的步骤实现则可以是不固定的。

3.UML类图

Android的设计模式-模板方法模式_第1张图片
模板方法UML类图.jpg
角色说明:

AbstractClass(抽象类):,定义了一整套算法框架。
ConcreteClass(具体实现类):具体实现类,根据需要去实现抽象类中的方法。

4.实现

继续以送快递为例,快递员送快递基本就是一套固定的流程:收到快递,准备派送->联系收货人->确定结果。

4.1 创建抽象类

定义算法框架,这里是快递员派送快递的步骤:

    public abstract class Postman {//抽象快递员类

        //派送流程
        public final void post() {//这里申明为final,不希望子类覆盖这个方法,防止更改流程的执行顺序
            prepare();//准备派送
            call();//联系收货人
            if (isSign())//是否签收
                sign();//签收
            else refuse();//拒签
        }

        protected void prepare() {//准备操作,固定流程,父类实现
            System.out.println("快递已达到,准备派送");
        }

        protected abstract void call();//联系收货人,联系人不一样,所以为抽象方法,子类实现

        protected boolean isSign() {//是否签收,这个是钩子方法,用来控制流程的走向
            return true;
        }

        protected void sign() {//签收,这个是固定流程,父类实现
            System.out.println("客户已签收,上报系统");
        }

        protected void refuse() {//拒签,空实现,这个也是钩子方法,子类可以跟进实际来决定是否去实现这个方法
        }
    }

需要注意的是上面的抽象类(Postman)包含了三种类型的方法:抽象方法、具体方法和钩子方法。

  • 抽象方法:需要子类去实现。如上面的call()
  • 具体方法:抽象父类中直接实现。如上面的prepare()sign()
  • 钩子方法:有两种,第一种,它是一个空实现的方法,子类可以视情况来决定是否要覆盖它,如上面的refuse();第二种,它的返回类型通常是boolean类型的,一般用于对某个条件进行判断,如果条件满足则执行某一步骤,否则将不执行,如上面的isSign()
4.2 创建具体实现类

根据需要去实现抽象类中的方法,下面以派送给两个不同的人为例,其中一个签收,另一个拒收:

    public class PostA extends Postman {//派送给A先生

        @Override
        protected void call() {//联系收货,实现父类的抽象方法
            System.out.println("联系A先生并送到门口");
        }
    }

    public class PostB extends Postman {//派送给B先生

        @Override
        protected void call() {//联系收货,实现父类的抽象方法
            System.out.println("联系B先生并送到门口");
        }

        @Override
        protected boolean isSign() {//是否签收,覆盖父类的钩子方法,控制流程的走向
            return false;
        }

        @Override
        protected void refuse() {//拒签,覆盖父类的钩子方法
            System.out.println("拒绝签收:商品不符");
        }
    }
4.3 客户端测试
    public void test(){
        System.out.println("----派送A----");
        Postman postA=new PostA();
        postA.post();
        System.out.println("----派送B----");
        Postman postB=new PostB();
        postB.post();
    }
输出结果:
----派送A----
快递已达到,准备派送
联系A先生并送到门口
客户已签收,上报系统
----派送B----
快递已达到,准备派送
联系B先生并送到门口
拒绝签收:商品不符

5. 应用场景

  • 一次性实现算法的执行顺序和固定不变部分,可变部分则交由子类来实现。
  • 多个子类中拥有相同的行为时,可以将其抽取出来放在父类中,避免重复的代码。
  • 使用钩子方法来让子类决定父类的某个步骤是否执行,实现子类对父类的反向控制。
  • 控制子类扩展。模板方法只在特定点调用钩子方法,这样就只允许在这些点进行扩展。

6. 优点

  • 提高代码复用性,去除子类中的重复代码。
  • 提高扩展性,不同实现细节放到不同子类中,易于增加新行为。

7. 缺点

每个不同的实现都需要定义一个子类,这会导致类的个数的增加,设计更加抽象。

8. Android中的源码分析

Android中View的draw方法就是使用了模板方法模式:

8.1 View的draw方法

public class View{
    //钩子方法,空实现
    protected void onDraw(Canvas canvas) {
    }
    //钩子方法,空实现
    protected void dispatchDraw(Canvas canvas) {
    }
    //绘制方法,定义绘制流程
    public void draw(Canvas canvas) {
       //其他代码略

        /*
         *  绘制流程如下:
         *
         *      1. 绘制view背景
         *      2. 如果有需要,就保存图层
         *      3. 绘制view内容
         *      4. 绘制子View
         *      5. 如果有必要,绘制渐变框和恢复图层
         *      6. 绘制装饰(滑动条等)
         */

        if (!dirtyOpaque) {
            drawBackground(canvas);//步骤1. 绘制view背景
        }

        // 如果可能的话跳过第2步和第5步(常见情况)
        final int viewFlags = mViewFlags;
        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
        if (!verticalEdges && !horizontalEdges) {
            if (!dirtyOpaque) onDraw(canvas);//步骤3. 绘制view内容

            dispatchDraw(canvas);//步骤4. 绘制子View

            // 覆盖一部分内容,绘制前景
            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }

            onDrawForeground(canvas); //步骤6. 绘制装饰(滑动条等)

            return;
        }
}

8.2 说明

  • Viewdraw()方法中定义了一整套的绘制流程,这个流程是固定的,所有的Android中的View都是按照这个流程来绘制的。其中drawBackground()这个方法在View类中是实现了具体过程的,而onDraw()方法和dispatchDraw()方法在View中都是空实现,即都是钩子方法。不同的子类通过重写这些空实现来实现自身不同的绘制效果。
  • 具体的View,像TextView这些单一的View,就会重写onDraw()方法,由于TextView没有子View,所以dispatchDraw()还是空实现;而ViewGroup类含有子View,需要遍历子View并绘制,因此需要重写onDraw()dispatchDraw()
  • 所以,我们自定义View时必须且只需重写onDraw();自定义ViewGroup时则需要重写onDraw()dispatchDraw()

8.3 其他

另外,像Activity的生命周期,AsyncTask等等也是用到了模板方法模式,也兴趣的可以研究一下。

相关文章阅读
Android的设计模式-设计模式的六大原则
一句话总结23种设计模式则
创建型模式:
Android的设计模式-单例模式
Android的设计模式-建造者模式
Android的设计模式-工厂方法模式
Android的设计模式-简单工厂模式
Android的设计模式-抽象工厂模式
Android的设计模式-原型模式
行为型模式:
Android的设计模式-策略模式
Android的设计模式-状态模式
Android的设计模式-责任链模式
Android的设计模式-观察者模式
Android的设计模式-模板方法模式
Android的设计模式-迭代器模式
Android的设计模式-备忘录模式
Android的设计模式-访问者模式
Android的设计模式-中介者模式
Android的设计模式-解释器模式
Android的设计模式-命令模式
结构型模式:
Android的设计模式-代理模式
Android的设计模式-组合模式
Android的设计模式-适配器模式
Android的设计模式-装饰者模式
Android的设计模式-享元模式
Android的设计模式-外观模式
Android的设计模式-桥接模式

你可能感兴趣的:(Android的设计模式-模板方法模式)