资讯专栏INFORMATION COLUMN

模板方法模式

Scorpion / 3155人阅读

摘要:嗯,模板模式应该是跟杨洋一样帅,所以带着这份爱慕,我们一起来看看它到底有没有比杨洋还要帅模板方法模式是什么模板方法模式是一种只需使用继承就可以实现的非常简单的模式。模板方法模式由两部分结构组成,第一部分是抽象父类,第二部分是具体实现的子类。

JavaScript-模板方法模式

模板方法是什么鬼?模板模式又是什么鬼?? 听说它很复杂,听说它很难,我可不可以不学啊?。刷了一会儿微博,这几天杨洋的新电影《三生三世十里桃花》上映了,不行,我要去看,虽然闺蜜说不好看,但是,去看杨洋也是可以的,嘻嘻嘻。 嗯,模板模式应该是跟杨洋一样帅,所以带着这份爱慕,我们一起来看看它到底有没有比杨洋还要帅!!

模板方法模式是什么?

模板方法模式是一种只需使用继承就可以实现的非常简单的模式。这是它的一种定义,简单吗?不觉得。?  

说到继承,我们一定会能够想到它肯定有父亲,不然要怎么继承。模板方法模式由两部分结构组成,第一部分是抽象父类,第二部分是具体实现的子类。完了,又出来一个概念,抽象类?别怕,我们一一来讲解。在模板方法模式中,子类实现中的相同部分被上移到父类中,而将不同的部分留给子类来实现。

完了,一不小心扔了这么多概念,你肯定晕了,不怕,不怕,上实例了哈。?
先泡一杯咖啡吧!

首先我们先来泡一杯咖啡,一般来说,泡咖啡的步骤通常如下:

1.先把水煮沸;

2.用沸水冲泡咖啡;

3.把咖啡倒进杯子;

4.加糖和牛奶。

我们用es5来得到一杯香浓的咖啡吧:

var Coffee=function(){}
Coffee.prototype.boilWater=function(){
    console.log("水煮开了");
}
Coffee.prototype.brewCoffeeGriends=function(){
    console.log("用沸水冲泡咖啡");
}
Coffee.prototype.pourInCup=function(){
    console.log("把咖啡倒进杯子");
}
Coffee.prototype.addSugarAndMilk=function(){
    console.log("加糖和牛奶");
}
// 封装 将实现的细节交给类的内部
Coffee.prototype.init = function() {
            this.boilWater();
            this.brewCoffeeGriends();
            this.pourInCup();
            this.addSugarAndMilk();
}
var coffee=new Coffee();
coffee.init();

如我们所愿了,控制台会输出泡茶的流程,和我们写下的一样。

我想喝茶吖,想喝茶。不急,不急,我们再来泡茶哈!

泡一壶茶啦!

其实呢,泡茶的步骤跟泡咖啡的步骤相差不大,大致是这样的:

1.把水煮沸;

2.用沸水浸泡茶叶;

3.把茶水倒进杯子;

4.加柠檬。

来,咱用es6来泡茶:

class Tea{
    constructor(){

    }
    boilWater(){
        console.log("把水烧开");
    }
    steepTeaBag(){
        console.log("浸泡茶叶");
    }
    pourInCup(){
        console.log("倒进杯子");
    }
    addLemon(){
        console.log("加柠檬");
    }
    init(){
            this.boilWater();
            this.steepTeaBag();
            this.pourInCup();
            this.addLemon();
    }
}
var tea=new Tea();
tea.init();

又如我们所愿了,控制台输出了泡茶的流程。

思考啦!

现在到了思考的时间,我们刚刚泡了一杯咖啡和一壶茶,有没有觉得这两个过程是大同小异的。我们能很容易的就找出他们的共同点,不同点就是原料不同嘛,茶和咖啡,我们可以把他们抽象为"饮料"哇;泡的方式不同嘛,一个是冲泡,一个是浸泡,我们可以把这个行为抽象为"泡";加入的调料也不同咯,加糖和牛奶,加柠檬,它们也可以抽象为"调料"吖。

这么一分析,是不是很清楚了吖,我们整理一下就是:

1.把水煮沸;

2.用沸水冲泡饮料;

3.把饮料倒进杯子;

4.加调料。

大家请注意!大家请注意!主角来了!之前我们已经扔出了概念,所以我们现在可以创建一个抽象父类来表示泡一杯饮料的过程。那么,抽象父类?
抽象类?

抽象类是不能被实例化的,一定是用来继承的。继承了抽象类的所有子类都将拥有跟抽象类一致的接口方法,抽象类的主要作用就是为它的子类定义这些公共接口。

通过上面分析,这里具体来说就是要把泡茶和泡咖啡的共同步骤共同点找出来,封装到父类,也就是抽象类中,然后不同的步骤写在子类中,也就是茶和咖啡中。抽象类既然不能被实例化,不怕啊,子类就是他的实例化。

来吧!泡饮料啦!
var Beverage=function(){}
Beverage.prototype.boilWater=function(){
        console.log("把水煮沸");
}
Beverage.prototype.brew=function(){};
Beverage.prototype.pourInCup=function(){};
Beverage.prototype.addCondiments=function(){};
// 抽象方法
Beverage.prototype.init=function(){
    this.boilWater();
    this.brew();
    this.pourInCup();
    this.addCondiments();
}
var Coffee=function(){
    // 将父类的构造方法拿来执行一下
    Beverage.apply(this,arguments);
    // 就像es6的super执行 执行后this才会有对象的属性
}
Coffee.prototype=new Beverage();
var coffee=new Coffee();
coffee.init();
var Tea=function(){

}
Tea.prototype=new Beverage();
Tea.prototype.brew=function(){
    console.log("用沸水浸泡茶叶");
}
Tea.prototype.pourInCup=function(){
    console.log("把茶叶倒进杯子");
}
Tea.prototype.addCondiments=function(){
    console.log("加柠檬");
}
var tea=new Tea();
tea.init();

这里既泡了咖啡又泡了茶,是不是没有之前那么繁琐呢,这里的代码可是很高级的呢。

这里用一个父类Beverage来表示Coffee和Tea,然后子类就是后面的Coffee和Tea啦,因为这里的Beverage是一个抽象的存在,需要子类来继承它。泡饮品的流程,可以理解为一个模板模式 ,抽象类Beverage, 抽象方法init()在子类中实现。js的继承是基于原型链的继承,这里prototype就是类的原型链。这里由于coffee对象和tea对象的原型prototype上都没有对应的init(),所以请求会顺着原型链,找到父类Beverage的init()。子类寻找对应的属性和方法的时候会顺着原型链去查找,先找自己,没有找到会顺着去父类里面查找。

Beverage.prototype.init被称为模板方法的原因是,该方法中封装了子类的算法框架,它作为一个算法的模板,指导子类以何种顺序去执行哪些方法。

闭包实现模板方法模式

闭包也可以实现的哟,大家对闭包不太理解的可以参考我之前的文章:作用域闭包

var Beverage=function(param){
    // 局部变量
    var boilWater=function(){
       console.log("把水煮沸");
    }
    // 配置
    var brew=param.brew||function(){
        throw new Error("必须传递brew方法");
    }
    var pourInCup=param.pourInCup||function(){
        throw new Error("必须传递pourInCup方法")
    }
    var addCondiments=param.addCondiments||function(){
        throw new Error("必须传递addCondiments方法")
    } 
    var F=function(){};//对象 类
    F.prototype.init=function(){
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    };
    return F;
}
// 传对象
var Coffee=Beverage({
    brew:function(){
        console.log("用沸水泡咖啡");
    },
    pourInCup:function(){
        console.log("把咖啡倒进杯子");
    },
    addCondiments:function(){
        console.log("加糖和牛奶");
    }
})
var coffee=new Coffee(); 
coffee.init();

js 把抽象类改为配置类,param将成为Beverage函数里面的闭包函数的引用。Beverage是模板,把他变成一个可以配置的类把参数param传进来,就玩成了配置。这里的配置就是给Beverage传参数,也就是param。F对象规范了类的构成(流程),  里面的四个私有变量是闭包的。当Beverage new 的时候会new 一个F, 然后就调用了闭包 ,四个私有变量也被调用。

父类里面的brew pourInCup addCondiments方法都是空的,所以子类必须重写。父类里面的那三个方法是如果子类没用重写该方法,就会直接抛出一个异常那个,程序在运行时会得到一个错误。

小结

模板方法模式时一种典型的通过封装变化提高系统扩展性的设计模式。运用了模板方法模式的程序中,子类方法种类和执行顺序都是不变的,但是子类的方法具体实现则是可变的。父类是个模板,子类可以添加,就增加了不同的功能。子类和父类之间也就是"尧舜禹"之间的关系,属于同一类,没有血缘关系。

哇,到这里帅哥就看完啦,是不是很帅,是不是很有意思,不行,我要去看我的杨洋了,抛弃我的杨洋。?

有建议或者觉得有错误的地方可以指出来哟,帅哥要分享,学习也要分享的。共同进步吧!?

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/84647.html

相关文章

  • 我的Java设计模式-模板方法模式

    摘要:模板方法模式定义定义抽象类并且声明一些抽象基本方法供子类实现不同逻辑,同时在抽象类中定义具体方法把抽象基本方法封装起来,这就是模板方法模式。 近日,ofo小黄车宣布入驻法国巴黎,正式进入全球第20个国家,共享单车已然改变了我们的出行方式。就拿我自己来说,每当下班出地铁的第一件事,以光速锁定一辆共享单车,百米冲刺的速度抢在别人之前占领它。 而大家都是重复着同样的动作,拿出手机开锁、骑车、...

    levius 评论0 收藏0
  • Java设计模式-模板方法模式

    摘要:重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数详见后面的扩展示例约束其行为。 定义 Define the skeleton of an algorithm in an operation,deferring some steps to subclasses.TemplateMethod lets subclasses redefine certai...

    hellowoody 评论0 收藏0
  • 架构师的工具-模板方法模式

    摘要:也是一些架构师常用的模式。写出方法,需要子类自己定义穿衣服需要子类自定义洗脸刷牙子类自定义方法出门准备工作需要子类自定义出门穿阿迪吊丝的衣服整理文件,找工作恩,这个模板,差不多能满足正常人的需求。 所谓的模板就是一个重用一万次都不会觉得有问题的代码。 在es6中,提出了一个 ``反引号的书写方式--又叫做模板字符串.他最大的功能就是用来书写模板html的.通常在js中使用模板是 T...

    roundstones 评论0 收藏0
  • js设计模式--模板方法模式

    摘要:前言本系列文章主要根据设计模式与开发实践整理而来,其中会加入了一些自己的思考。模板方法模式由两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类。 前言 本系列文章主要根据《JavaScript设计模式与开发实践》整理而来,其中会加入了一些自己的思考。希望对大家有所帮助。 文章系列 js设计模式--单例模式 js设计模式--策略模式 js设计模式--代理模式 js设计模式--迭...

    yuanxin 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<