JavaScript模板方法模式

JavaScript模板方法模式

  • 1 什么是模板方法模式
  • 2 Coffee or Tea
  • 3 钩子方法
  • 4 好莱坞原则

1 什么是模板方法模式

模板方法模式是一种只需使用继承就可以实现的模式。

模板方法模式由两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类。

通常在抽象父类中封装了子类的算法框架,包括实现一些公共方法以及封装子类中所有方法的执行顺序。子类通过继承这个抽象类,也继承了整个算法结构,并且可以选择重写父类的方法。

2 Coffee or Tea

接下来通过咖啡与茶这个经典的例子来说明模版方法模式。

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

  1. 把水煮沸
  2. 用沸水冲泡咖啡
  3. 将咖啡倒入杯子中
  4. 添加糖和牛奶

将上述步骤写成代码描述出来,具体如下:

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. 添加柠檬

同样用一段代码来实现泡茶的步骤:

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

经过对比泡咖啡和泡茶的过程,我们发现两者的步骤好像差不多,但是有如下不同点:

  • 原料不同。一个是咖啡,一个是茶,但我们可以把它们都抽象为“饮料”;
  • 泡的方式不同。咖啡是冲泡,而茶叶是浸泡,我们可以把它们都抽象为“泡”;
  • 加入的调料不同。一个是糖和牛奶,一个是柠檬,但我们可以把它们都抽象为“调料”;

经过抽象之后,不管是泡咖啡还是泡茶,我们都能整理为下面四步:

  1. 把水煮沸
  2. 用沸水冲泡饮料
  3. 将饮料倒入杯子中
  4. 添加调味料

根据上述共同点,我们可以创建一个抽象父类来表示泡一杯饮料的整个过程。不论是Coffee,还是Tea,都用Beverage来表示,代码如下:

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 () {};
Coffee.prototype = new Beverage();// 重写父类方法
Coffee.prototype.brew = function () {console.log("用沸水冲泡咖啡");
};
Coffee.prototype.pourInCup = function () {console.log("把咖啡倒进杯子");
};
Coffee.prototype.addCondiments = function () {console.log("加糖和牛奶");
};var Coffee = new Coffee();
Coffee.init();
var Tea = function () {};
Tea.prototype = new Beverage();// 重写父类方法
Tea.prototype.steepTeaBag = function () {console.log("用沸水浸泡茶叶");
};
Tea.prototype.pourInCup = function () {console.log("把茶水倒进杯子");
};
Tea.prototype.addCondiments = function () {console.log("加柠檬");
};var tea = new Tea();
tea.init();

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

3 钩子方法

通过模板方法模式,我们在父类中封装了子类的算法框架。这些算法框架在正常状态下是适用于大多数子类的,但如果有一些特别“个性”的子类呢?比如有一些客人喝咖啡是不加调料(糖和牛奶)的。

既然Beverage作为父类,已经规定好了冲泡饮料的4个步骤,那么有什么办法可以让子类不受这个约束呢?

钩子方法可以用来解决这个问题,放置钩子是隔离变化的一种常见手段。我们在父类中容易变化的地方放置钩子,钩子可以有一个默认的实现,究竟要不要“挂钩”,这由子类自行决定。钩子方法的返回结果决定了模板方法后面部分的执行步骤,也就是程序接下来的走向,这样一来,程序就拥有了变化的可能。

接下来将挂钩customerWantsCondiments放入Beverage类,看看我们如何得到一杯不需要糖和牛奶的咖啡,代码如下:

var Beverage = function () {};
Beverage.prototype.boilWater = function () {console.log("把水煮沸");
};
Beverage.prototype.brew = function () {}; // 冲泡动作
Beverage.prototype.pourInCup = function () {}; // 倒入杯子
Beverage.prototype.addCondiments = function () {}; // 添加调料
Beverage.prototype.customerWantsCondiments = function () {return true; // 默认需要调料
};
Beverage.prototype.init = function () {this.boilWater();this.brew();this.pourInCup();if (this.customerWantsCondiments()) { // 如果返回true,表示需要调料this.addCondiments();}
};
var Coffee = function () {};
Coffee.prototype = new Beverage();
// 重写父类方法
Coffee.prototype.brew = function () {console.log("用沸水冲泡咖啡");
};
Coffee.prototype.pourInCup = function () {console.log("把咖啡倒进杯子");
};
Coffee.prototype.addCondiments = function () {console.log("加糖和牛奶");
};
Coffee.prototype.customerWantsCondiments = function () {return window.confirm("请问需要调料吗?");
};
var Coffee = new Coffee();
Coffee.init();

4 好莱坞原则

JS好莱坞原则的基本思想是,高层组件通过调用底层组件暴露的接口来控制底层组件的行为,而底层组件不应该直接依赖或调用高层组件的实现。

这个原则的名字来源于好莱坞电影制片厂,因为在好莱坞电影中,明星演员通常是高层组件,而服装、化妆等其他组件则是底层组件。这些底层组件在电影制作过程中由高层组件来控制和协调。

在这一原则的指导下,我们允许底层组件将自己挂钩到高层组件中,而高层组件会决定什么时候、以何种方式去使用这些底层组件。

模板方法模式是好莱坞原则的一个典型使用场景,它与好莱坞原则的联系非常明显,当我们用模板方法模式编写一个程序时,就意味着子类放弃了对自己的控制权,而是改为父类通知子类,哪些方法应该在什么时候被调用。作为子类,只负责提供一些设计上的细节。

在好莱坞原则的指导之下,下面这段代码可以达到和继承一样的效果。

var Beverage = function (param) {var boilWater = function () {console.log("把水煮沸");};var brew = param.brew; // 冲泡动作var pourInCup = param.pourInCup; // 倒入杯子var addCondiments = param.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 Tea = Beverage({brew: function () {console.log("用沸水浸泡茶叶");},pourInCup: function () {console.log("把茶倒进杯子");},addCondiments: function () {console.log("加柠檬");},
});var coffee = new Coffee();
coffee.init();
var tea = new Tea();
tea.init();

在这段代码中,我们把brewpourInCupaddCondiments这些方法依次传入Beverage函数,Beverage函数被调用之后返回构造器FF类中包含了“模板方法”F.prototype.init。跟继承得到的效果一样,该“模板方法”里依然封装了饮料子类的算法框架。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/131003.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI:60-基于深度学习的瓜果蔬菜分类识别

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…

力扣第139题 单词拆分 c++ 附java代码 动态规划题型

题目 时间复杂度为O(n^2),其中n为字符串s的长度。这是因为我们需要遍历字符串s的每个位置,对于每个位置i,又需要从0到i-1的位置进行遍历,因此总的时间复杂度为O(n^2)。 空间复杂度为O(n),其中n为字符串s的长度。这是因…

网络基础扫盲-多路转发

博客内容:多路转发的常见方式select,poll,epoll 文章目录 一、五种IO模型二、多路转发的常见接口1.select2、poll3、epoll 总结 前言 Linux下一切皆文件,是文件就会存在IO的情况,IO的方式决定了效率的高低。 一、五种…

docker的使用以及注意事项

ssh的登录 1.登录ssh ssh 用户名IP地址 2.生成密钥 ssh-keygen生成密钥,在.ssh文件夹中(如果没有自己生成一个) 如果密钥之前已经生成过,可能在配置git的时候,会报错:这个密钥已经使用过的报错 解决方法是:otherwise[…

基于java+springboot+vue在线选课系统

项目介绍 本系统结合计算机系统的结构、概念、模型、原理、方法,在计算机各种优势的情况下,采用JAVA语言,结合SpringBoot框架与Vue框架以及MYSQL数据库设计并实现的。员工管理系统主要包括个人中心、课程管理、专业管理、院系信息管理、学生…

随机微分方程的分数扩散模型 (score-based diffusion model) 代码示例

随机微分方程的分数扩散模型(Score-Based Generative Modeling through Stochastic Differential Equations) 基于分数的扩散模型,是估计数据分布梯度的方法,可以在不需要对抗训练的基础上,生成与GAN一样高质量的图片。…

Cube MX 开发高精度电流源跳坑过程/SPI连接ADS1255/1256系列问题总结/STM32 硬件SPI开发过程

文章目录 概要整体架构流程技术名词解释技术细节小结 概要 1.使用STM32F系列开发一款高精度恒流电源,用到了24位高精度采样芯片ADS1255/ADS1256系列。 2.使用时发现很多的坑,详细介绍了每个坑的具体情况和实际的解决办法。 坑1:波特率设置…

如何使用Ruby 多线程爬取数据

现在比较主流的爬虫应该是用python,之前也写了很多关于python的文章。今天在这里我们主要说说ruby。我觉得ruby也是ok的,我试试看写了一个爬虫的小程序,并作出相应的解析。 Ruby中实现网页抓取,一般用的是mechanize,使…

Pytorch从零开始实战08

Pytorch从零开始实战——YOLOv5-C3模块实现 本系列来源于365天深度学习训练营 原作者K同学 文章目录 Pytorch从零开始实战——YOLOv5-C3模块实现环境准备数据集模型选择开始训练可视化模型预测总结 环境准备 本文基于Jupyter notebook,使用Python3.8&#xff0c…

webJS基础-----制作一个时间倒计时

1,可以使用以下两个方式制作 方式1:setTimeout ()定时器是在指定的时间后执行某些代码,代码执行一次就会自动停止; 方式2:setInterval ()定时器是按照指定的周期来重复执行某些代码,该定时器不会自动停止…

DL Homework 6

目录 一、概念 (1)卷积 (2)卷积核 (3)特征图 (4)特征选择 (5)步长 (6)填充 (7)感受野 二、探究不同卷…

【开题报告】基于uniapp的在线考试小程序的设计与实现

1.研究背景 随着社会的发展和科技的进步,网络技术被广泛应用于教育领域。在线教育已成为当今发展趋势之一,其中在线考试更是具有重要的意义。传统的考试方式不仅耗费大量人力物力,而且存在考试成果的保密问题。而在线考试可以使考试过程更加…

JVM运行时数据区-堆

目录 一、堆的核心概述 (一)概述 (二)堆空间细分 (三)jvisualvm工具 二、设置堆内存的大小与OOM 三、年轻代与老年代 四、图解对象分配一般过程 五、对象分配特殊过程 六、常用调优工具 七、Mino…

手搓一个ubuntu自动安装python3.9的sh脚本

#!/bin/bash# Step 1: 更新系统软件包 sudo apt update sudo apt upgrade -y sudo apt install -y software-properties-common# Step 2: 安装Python 3.9的依赖项 sudo apt install -y build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libread…

leetCode 416.分割等和子集 + 01背包 + 动态规划 + 记忆化搜索 + 递推 + 空间优化

关于此题我的往期文章: LeetCode 416.分割等和子集(动态规划【0-1背包问题】采用一维数组dp:滚动数组)_呵呵哒( ̄▽ ̄)"的博客-CSDN博客https://heheda.blog.csdn.net/article/details/133212716看本期文章时&…

使用udevdm查询蓝牙模块的信息

1.首先查询蓝牙设备在系统中的设备路径 udevadm info --querypath -n /dev/ttyS1 2.查询蓝牙设备的所有信息包括父设备信息 EMUELEC:~ # udevadm info -ap /devices/platform/ffd24000.serial/tty/ttyS1 备注:查询设备所有信息 udevadm info --queryall -n /dev…

关于JADX和JEB的小问题

关于JADX和JEB的小问题 很久没水过技术文啦,最近也刚好遇到点小问题,特此记录 第一个问题 在处理app加密逻辑的时候一直拿不到正确的密文,反复看了反编译出来的代码(如下图) public static string n(String str, Stri…

基础课22——云服务(SaaS、Pass、laas、AIaas)

1.云服务概念和类型 云服务是一种基于互联网的计算模式,通过云计算技术将计算、存储、网络等资源以服务的形式提供给用户,用户可以通过网络按需使用这些资源,无需购买、安装和维护硬件设备。云服务具有灵活扩展、按需使用、随时随地访问等优…

linux 查看当前目录下每个文件夹大小

要在 Linux 中查看当前目录下每个文件夹的大小,可以使用 du 命令(磁盘使用情况)结合其他一些选项。下面是几个常用的命令示例: 显示当前目录下每个文件夹的大小——只显示一层文件夹: du -h --max-depth1该命令会以人…

2023年内衣行业分析:京东大数据平台-服饰内衣市场解析

如今,女性消费力的提升正在推动国内女性内衣市场份额逐年提升。而今年,内衣市场更是进入了存量之战,增长趋势明显减弱。 根据鲸参谋数据显示,今年1月至9月,京东平台内衣(文胸)累计销量约500万件…