设计模式——模板方法模式学习

247 阅读4分钟

模板方法模式学习

一、定义和组成

模板方法模式有两部分组成

  • 抽象父类
  • 具体实现的子类

通常是在父类中定义算法骨架和一些公共的方法,子类继承父类方法的基础上,不修改算法骨架,重新定义父类中算法的某些实现步骤。

二、应用场景

  • 多个子类拥有公共的方法,且逻辑相同,可以提取到父类中实现
  • 父类实现不变的部分,并将可变行为交给子类来实现
  • 需要通过子类来决定父类中某个步骤是否执行,实现子类对父类的反控制

三、例子讲解

我们去银行取钱、存钱、办卡等业务的时候,都需要三大步骤:

  1. 排队取号;
  2. 办理具体的业务;
  3. 服务评价。

然后这个步骤可以抽取到父类中去实现整体的步骤(算法骨架),其中第一步排队取号和第三步服务评价是公共的方法,可以在父类中实现,第二步办理具体业务在不同的子类来实现

3.1 存钱

首先我们来看下银行存钱业务,一般步骤如下:

  1. 排队取号;
  2. 存钱;
  3. 服务评价。

通过以下代码,我们就完成了去银行办理存钱的业务

       var Deposit = function () {};
        
        Deposit.prototype.takeNumber = function() {
            console.log('排队取号');
        };

        Deposit.prototype.depositMoney = function() {
            console.log('存钱');
        };

        Deposit.prototype.serviceEvaluation = function() {
            console.log('服务评价');
        };

        Deposit.prototype.init = function() {
            this.takeNumber();
            this.depositMoney();
            this.serviceEvaluation();
            console.log('完成银行业务办理');
        };
        
        var deposit = new Deposit();
        deposit.init();

3.2 取钱

接下来是去银行取钱业务(当然现在都是大笔业务才需要柜台,小笔金额可以直接在ATM操作哈~~),一般步骤如下:

  1. 排队取号;
  2. 取钱;
  3. 服务评价。

通过以下的代码,我们就完成了去银行办理取钱的业务

        var Withdraw = function () {};

        Withdraw.prototype.takeNumber = function() {
            console.log('排队取号');
        };
        
        Withdraw.prototype.withdrawMoney = function() {
            console.log('取钱');
        };

        Withdraw.prototype.serviceEvaluation = function() {
            console.log('服务评价');
        };

        Withdraw.prototype.init = function() {
            this.takeNumber();
            this.withdrawMoney();
            this.serviceEvaluation();
            console.log('完成银行业务办理');
        };
        
        var withdraw = new Withdraw();
        withdraw.init();

3.3 分离共同点

现在我们分别完成了存钱和取钱的银行业务,通过思考和比较,我们发现去银行办理业务的流程是大同小异的,如

存钱 取钱
排队取号 排队取号
存钱 取钱
服务评价 服务评价

通过对比发现以下不同点:

  • 办理的业务不同。

不管是取钱还是存钱,我们都可以把它抽象为银行业务。那么整体的业务流程步骤大概是以下三步:

  1. 排队取号;
  2. 办理银行业务;
  3. 服务评价。

我们创建一个父类银行业务类来处理银行业务办理的整个过程,不管是取钱还是存钱或者是办卡,我们都用handleBusiness的方法来表示,具体代码如下:

        var BankBusiness = function () {};
        
        BankBusiness.prototype.takeNumber = function() {
            console.log('排队取号');
        };

        BankBusiness.prototype.handleBusiness = function() {
            // 空方法 由子类重写
        };

        BankBusiness.prototype.serviceEvaluation = function() {
            console.log('服务评价');
        };

        BankBusiness.prototype.init = function() {
            this.takeNumber();
            this.handleBusiness();
            this.serviceEvaluation();
            console.log('完成银行业务办理');
        };

创建存钱子类,并让它继承银行业务类:

        var Deposit = function () {};
        Deposit.prototype = new  BankBusiness();

接下来要重写抽象父类中的一些方法,排队取号和服务评价可以直接使用父类BankBusiness中的takeNumber方法和serviceEvaluation方法,handleBusiness方法都需要在Deposit子类中重写,代码如下:


        Deposit.prototype.handleBusiness = function () {
            console.log('存钱');
        }

        var deposit = new Deposit();
        deposit.init();

至此我们的Deposit类已经完成了,当调用deposit对象的init方法时,由于deposit对象和Deposit构造器的原型prototype上都没有对应的init方法,所以该请求会顺着原型链,被委托给Deposite的“父类”BankBusiness原型上的init方法。而BankBusiness.prototype.init方法中已经规定好了银行办理业务的顺序,所以我们能成功地办理取钱业务,代码如下:

        BankBusiness.prototype.init = function() {
            this.takeNumber();
            this.handleBusiness();
            this.serviceEvaluation();
            console.log('完成银行业务办理');
        };

通过上面的例子,我们可以得知BankBusiness.prototype.init就是我们说的模板方法。 BankBusiness.prototype.init被称为模板方法的原因是,该方法中封装了子类的算法框架,它作为一个算法的模板,指导子类以何种顺序去执行哪些方法。BankBusiness.prototype.init方法中,算法内的每一个步骤都清楚地展示在我们眼前。