JS设计模式之模板方法模式:打破束缚,解密代码复用的精髓

news2024/9/27 15:49:54

image.png

一. 前言

在前端开发中,模板方法模式是一种常用的设计模式,它能够有效地提高代码的复用性和扩展性。在 JavaScript 中,模板方法模式的应用广泛,常被用于组件的生命周期管理、请求封装和拦截器设计、表单验证等多个场景。

本篇文章将详细介绍 JavaScript 模板方法模式,说明其在前端开发中的实际应用。理解模板方法模式的核心思想和基本结构,针对不同的应用场景进行研究。通过深入理解模板方法模式,我们将能够更好地运用这一设计模式来提升我们的前端开发效率和代码质量。

二. 什么是模版方法模式

1. 基本概念

模版方法模式(Template Method Pattern)是一种行为型设计模式,它允许我们定义一个算法的骨架,但将一些步骤的具体实现延迟到子类中。该模式通过使用继承和多态来实现,以便在不改变算法结构的情况下,允许子类对某些步骤进行自定义。

总结起来,模版方法模式是一种通过定义算法骨架和延迟具体步骤实现的设计模式。它的目的是促进代码复用和扩展性,使得相似的算法能够在不同的子类中灵活自定义。

2. 作用

模版方法模式的作用主要体现在以下几个方面:

  1. 代码复用:模版方法模式将算法的共同部分提取到抽象父类中,避免了重复编写相似的代码。通过定义模版方法和具体方法,可以减少重复代码的编写量,提高代码的复用性。

  2. 封装变化:模版方法模式将具体实现封装在子类中,隐藏了算法的具体细节。这样,在使用模版方法时,我们只需要关注抽象父类的方法调用,而无需关心具体的实现细节,提高了代码的可读性和可维护性。

  3. 灵活框架设计:模版方法模式常用于框架的设计中,通过定义抽象父类和具体子类,框架可以提供一个统一的算法结构,同时允许用户根据具体需求来自定义实现。这样可以提供一个灵活且易于扩展的框架,满足不同场景下的需求。

综上所述,JavaScript 模版方法模式的作用是提高代码的复用性、提供灵活性和扩展性、封装变化以及在框架设计中的应用。它能够使代码更加易读、易维护,并能够适应变化和需求的变更。

三. 基本结构和核心概念

在模版方法模式中,有一个抽象的父类(也可以是接口),其中定义了一个模版方法(Template Method)。模版方法是一个结构化的方法,它包含了算法的骨架,但其中的某些具体步骤是抽象的,需要由子类来实现。除了模版方法外,抽象父类还可以包含其他的具体方法。

image.png

模板方法模式的基本结构由以下几个要素组成:

  1. 抽象类(Abstract Class):抽象类是模板方法模式的核心,它定义了算法的骨架和流程。在 JavaScript 中,我们可以使用普通的对象来充当抽象类。抽象类中包含一个或多个抽象方法或钩子方法,用于规定算法中的特定步骤。

  2. 抽象方法(Abstract Method):抽象方法是抽象类中的方法,它没有具体的实现,需要在子类中进行具体的实现。在 JavaScript 中,我们可以使用空函数或抛出异常的方式来表示抽象方法。

  3. 钩子方法(Hook Method):钩子方法是抽象类中的方法,它的默认实现为空函数,子类可以选择性地进行重写或扩展。钩子方法提供了一种灵活的方式,允许子类在不改变算法结构的情况下对流程进行个性化的定制。

  4. 具体类(Concrete Class):具体类是继承抽象类并实现其中的抽象方法的子类。具体类负责实现具体的算法步骤,完成特定的业务逻辑。

使用 JavaScript 模板方法模式的基本步骤如下:

  1. 定义抽象类,并在其中定义模板方法。模板方法包含算法的骨架,它通过调用抽象方法或钩子方法来完成具体的操作。

  2. 定义抽象方法或钩子方法。抽象方法没有具体的实现,需要在具体类中进行实现。钩子方法的默认实现为空函数,子类可以选择性地进行重写或扩展。

  3. 定义具体类,继承抽象类并实现其中的抽象方法。具体类负责具体的算法实现和业务逻辑。

  4. 在客户端代码中,创建具体类的实例,并调用模板方法来执行算法。模板方法会按照定义的算法结构来执行并调用抽象方法或钩子方法。

模板方法模式的基本结构可以通过以下代码示例进行说明:

// 定义抽象类
class AbstractClass {
  // 模板方法
  templateMethod() {
    this.primitiveOperation1();
    this.hookMethod();
    this.primitiveOperation2();
  }

  // 抽象方法
  primitiveOperation1() {
    throw new Error("Abstract method primitiveOperation1() must be overridden");
  }

  // 钩子方法
  hookMethod() {}

  // 抽象方法
  primitiveOperation2() {
    throw new Error("Abstract method primitiveOperation2() must be overridden");
  }
}

// 定义具体类
class ConcreteClass extends AbstractClass {
  primitiveOperation1() {
    console.log("具体类实现的primitiveOperation1");
  }

  hookMethod() {
    console.log("具体类覆盖的hookMethod");
  }

  primitiveOperation2() {
    console.log("具体类实现的primitiveOperation2");
  }
}

// 客户端代码
const concreteClass = new ConcreteClass();
concreteClass.templateMethod();

抽象类 AbstractClass 定义了模板方法 templateMethod(),并在其中调用了抽象方法 primitiveOperation1()primitiveOperation2(),以及钩子方法 hookMethod()

具体类 ConcreteClass 继承了抽象类,并实现了其中的抽象方法和钩子方法。在客户端代码中,我们创建了具体类的实例 concreteClass,并调用了模板方法 templateMethod()来执行算法。

通过这样的结构,模板方法模式可以在不同的场景中提供一种灵活且结构化的方式来管理和定制执行流程。

四. 实现方式

在 JavaScript 中,模版方法模式的实现通常是通过类的继承来实现的。抽象父类定义了模版方法和抽象方法,而具体的子类则通过继承抽象父类,并实现其中的抽象方法来完成具体的业务逻辑。

1. 使用类继承

通过定义一个抽象父类,并在其中定义模版方法、抽象方法、具体方法和钩子方法,在具体子类中实现抽象方法,并根据需要覆盖钩子方法。子类通过继承父类,获取父类中定义的算法骨架和共享代码逻辑。

class AbstractClass {
  templateMethod() {
    this.method1();
    this.method2();
    this.method3();
  }

  method1() {
    console.log("AbstractClass implementing method1");
  }

  method2() {
    console.log("AbstractClass implementing method2");
  }
  // 抽象方法,子类必须实现
  method3() {
    throw new Error("Abstract method method3 must be implemented");
  }
}

class ConcreteClass extends AbstractClass {
  method2() {
    console.log("ConcreteClass overriding method2 implementation");
  }

  method3() {
    console.log("ConcreteClass implementing method3");
  }
}

const concrete = new ConcreteClass();
concrete.templateMethod();
  • AbstractClass 是一个抽象类,其中定义了模板方法 templateMethod(),具体方法 method1()method2(),以及抽象方法 method3()

  • method1()method2() 是具体的实现,可以在抽象类中提供默认的实现,子类也可以根据需要进行覆盖。

  • method3() 是抽象方法,没有具体实现,子类必须实现该方法。

  • ConcreteClass 是具体子类,继承了 AbstractClass,并重写了 method2() 和实现了 method3()

  • concreteConcreteClass 的实例,可以调用 templateMethod() 方法来执行整个算法。

2. 使用对象组合

将算法的不同步骤分别封装为独立的函数或对象,在主函数中按照特定顺序调用这些函数或对象,完成整个算法的执行。这样可以通过组合不同的函数或对象来定制算法的特定步骤。

const step1 = () => {
  console.log("Step 1");
};

const step2 = () => {
  console.log("Step 2");
};

const step3 = () => {
  console.log("Step 3");
};

const algorithm = () => {
  step1();
  step2();
  step3();
};

algorithm();
  • step1()step2()step3() 是独立的函数,分别表示算法的不同步骤。

  • algorithm() 函数将这些步骤按照特定顺序执行,即调用 step1()step2()step3()

  • 最后调用 algorithm() 函数即可执行整个算法。

3. 使用闭包

通过定义一个具有私有状态和公共接口的闭包函数,将算法的骨架和具体步骤封装在闭包函数内部,通过返回一个包含算法接口的对象,将算法暴露给外部使用。外部可以根据需要传入不同的参数来定制算法的具体行为。

const algorithm = (step1, step2, step3) => {
  step1();
  step2();
  step3();
};

const step1 = () => {
  console.log("Step 1");
};

const step2 = () => {
  console.log("Step 2");
};

const step3 = () => {
  console.log("Step 3");
};

algorithm(step1, step2, step3);
  • algorithm() 是一个需要接收三个步骤函数作为参数的函数。

  • step1()step2()step3() 分别表示算法的不同步骤。

  • 在调用 algorithm() 函数时,传入具体的步骤函数来执行整个算法。

通过这些不同的实现方式,我们可以根据具体的需求和场景选择合适的方式。

使用类继承适合建立有继承关系的类结构,适用于有一些共享的代码逻辑;

使用对象组合更加灵活,可以根据需要组合不同的步骤函数或对象;

使用闭包方式比较简洁,适用于一些简单的算法场景。根据实际情况进行选择,并根据需要进行适当的调整和扩展。

需要注意的是,JavaScript 并没有内置的抽象类和接口的概念,因此在实现模版方法模式时,可以通过抽象父类中抛出异常或提供默认实现来模拟抽象方法和钩子方法的作用。同时,通过约定命名规范、文档说明等方式,来保证子类正确实现抽象方法和钩子方法。

五. 应用场景

在前端项目开发中,模板方法模式可以应用于以下几个常见的场景:

1. 组件生命周期管理

在前端框架(如 React、Vue 等)的组件开发中,组件的生命周期是非常重要的。模板方法模式可以用来管理组件的生命周期,从组件的创建、渲染、更新、销毁等各个阶段进行钩子函数的定义和调用。具体代码如下:

class Component {
  // 模板方法,定义组件的生命周期流程
  lifecycle() {
    this.beforeMount();
    this.render();
    this.mounted();
    // ...
  }

  // 钩子方法,具体组件实现可覆盖
  beforeMount() {
    // 组件挂载前的逻辑
  }

  render() {
    // 组件渲染逻辑
  }

  mounted() {
    // 组件挂载后的逻辑
  }

  // ...
}

在子类中,可根据实际需求重写模板方法中的钩子方法,以实现自定义的组件逻辑。

2. 请求封装和拦截器

在前端项目中,封装和管理请求是一个常见的需求。模板方法模式可以应用于请求的封装和拦截器的设计,通过定义模板方法来约定请求的流程,将公共的逻辑提取到模板方法中。具体代码如下:

class HttpRequest {
  request(url, data) {
    this.beforeRequest();
    // 发起请求的逻辑
    // ...
    this.afterRequest();
  }

  beforeRequest() {
    // 请求前的拦截逻辑
  }

  afterRequest() {
    // 请求完成后的处理逻辑
  }

  // ...
}

在子类或具体实现中,可以根据需要重写或扩展模板方法中的钩子方法,以实现自定义的请求逻辑。

3. 表单验证

在表单验证中,不同的字段可能会有不同的验证规则和逻辑。模板方法模式可以应用于表单验证的设计,通过定义模板方法来规范验证的流程,将公共的验证逻辑提取到模板方法中。具体代码如下:

class FormValidator {
  validate(formData) {
    this.beforeValidate();
    // 表单验证的逻辑
    // ...
    this.afterValidate();
  }

  beforeValidate() {
    // 验证前的预处理逻辑
  }

  afterValidate() {
    // 验证完成后的处理逻辑
  }

  // ...
}

在子类或具体实现中,可以根据需要重写或扩展模板方法中的钩子方法,以实现自定义的验证逻辑。

通过使用模板方法模式,可以将复杂的项目逻辑进行结构化和模块化,提高代码的可维护性和扩展性。在不同的场景下,可以根据需要选择适合的模板方法模式应用于前端项目开发中。

六. 优缺点

JavaScript 模板方法模式在前端应用中有以下优点和缺点:

1. 优点

  1. 结构清晰:模板方法模式将算法的骨架和具体实现分离,使代码结构更清晰、易于理解和维护。

  2. 代码复用:通过将固定的执行流程定义在模板方法中,可以避免代码重复,提高代码的复用性。

  3. 灵活可扩展:通过钩子方法的定义,可以在模板方法的基础上灵活地添加、替换或扩展某些步骤的实现,适应不同的需求变化。

  4. 可定制性:模板方法模式可以让子类或具体实现根据具体需求自定义覆盖模板方法中的抽象步骤,实现个性化的逻辑处理。

2. 缺点

  1. 代码量增加:使用模板方法模式会增加代码量,因为需要定义抽象类或基类、抽象方法或钩子方法,并在子类或具体实现中进行实现或覆盖。

  2. 过度抽象:过度使用模板方法模式可能会导致代码过于抽象,增加理解和维护的难度,需要权衡代码的抽象程度。

  3. 约束性较强:模板方法模式的使用是通过继承或实现来实现的,这意味着在某些场景下可能会限制代码的灵活性和可扩展性。

总的来说,JavaScript 模板方法模式在前端应用中能够有效地提高代码的复用性和可维护性,使代码结构更清晰和模块化。然而,过度使用模板方法模式可能会增加代码量和抽象程度,需要合理权衡使用的场景和代码的复杂度。

七. 结语

通过上面的总体分析,我们可以有一个清晰的了解,在 JavaScript 前端开发中,模板方法模式是一种非常实用的设计模式,在前端应用中也具有广泛的应用场景。通过合理地运用该模式,可以提高代码的复用性、可维护性和扩展性,使代码结构更清晰和模块化。

然而,需要注意合理权衡抽象程度和使用场景,避免过分使用模板方法模式而导致代码复杂化。希望本篇文章能够大家理解和应用 JavaScript 模板方法模式有所帮助。

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

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

相关文章

数据结构和算法之树形结构(3)

文章出处:数据结构和算法之树形结构(3) 关注码农爱刷题,看更多技术文章!! 四、平衡二叉树(接前篇) 上一章节讲到为了避免二叉查找树退化成链表后的极度不平衡带来的低效率而衍生出了平衡二叉树,平衡二叉树的严格定义…

CDGA|数据治理:策略与价值的深度融合

在当今这个数据驱动的时代,企业数据治理的重要性日益凸显。数据不仅是企业的核心资产,更是驱动业务决策、优化运营流程、创新产品服务的关键力量。然而,要让数据治理真正发挥价值,企业需要采取一系列策略来确保数据的准确性、完整…

Unity 资源 之 PoseAI 基于肌肉的姿势创作工具

Unity 资源 之 PoseAI 基于肌肉的姿势创作工具 一,前言二,资源包内容三,免费获取资源包 一,前言 Unity 开发者们,今天要为大家介绍一款极具创新性的工具 ——PoseAI。 PoseAI 是一种最先进的基于肌肉的姿势创作工具&…

Flink基本概念和算子使用

基础概念 Flink是一个框架和分布式处理引擎,用于对无界数据流和有界数据流进行有状态计算,它的核心目标是“数据流上的有状态计算”。 有界流和无界流 有界流:具有明确的开始和结束时间,数据量有限。适合使用批处理技术&#xf…

Elasticsearch实战宝典:从日志分析到实时数据监控,全方位解锁搜索与分析的无限可能!

Elasticsearch 是一个开源的分布式搜索和分析引擎,常用于处理大规模数据。它提供了强大的全文搜索、结构化搜索、实时统计分析等功能。以下是一些 Elasticsearch 的实战应用案例。 1. 日志分析系统 Elasticsearch 经常被用于集中式日志管理(Centralized…

使用SSE流式输出(Javaweb前后端实战)

目录 一.什么是SSE? 主要特点: 二.SSE的实现过程: 三.SSE的前端实现: 1.创建 EventSource 对象: 2.处理接收到的信息: 3.处理特定事件: 4.处理连接错误问题: 5.关闭连接&am…

网络安全的方方面面

目录 一、网络安全概述二、数据加密三、消息完整性与数字签名四、身份认证五、密钥分发中心(KDC)与证书认证(CA)六、防火墙与入侵检测系统七、网络安全协议八、网络安全攻防 -- 黑客攻击简要流程九、网络安全常用术语 一、网络安全概述 网络安全的基本特征:相对性、…

ArcGIS Desktop使用入门(三)常用工具条——拓扑(上篇:地图拓扑)

系列文章目录 ArcGIS Desktop使用入门(一)软件初认识 ArcGIS Desktop使用入门(二)常用工具条——标准工具 ArcGIS Desktop使用入门(二)常用工具条——编辑器 ArcGIS Desktop使用入门(二&#x…

基于Java的学生档案管理系统

基于springbootvue实现的学生档案管理系统 (源码L文ppt)4-065 第4章 系统设计 4.1 总体功能设计 学生档案管理系统的总体功能设计包括学生信息管理、课程管理、教师信息管理、成绩管理和系统配置管理。系统将提供用户友好的界面,支…

【Linux】图解详谈HTTPS的安全传输

文章目录 1.前置知识2.只使用对称加密3.只使用非对称加密 因为私钥加密只能公钥解开,公钥加密只能私钥解开4.双方都是使用非对称加密5.非对称加密 对称加密6.非对称加密对称加密CA认证(一)CA认证(二)https &#xff0…

耳夹式耳机哪个牌子好?主流耳夹式耳机推荐,多维度耳夹式耳机测评

耳夹式耳机哪个牌子好?耳机已经逐渐走进了众多消费者的生活,可以非常便捷地满足人们的对声音的需求,无论是在享受音乐还是接听电话方面都非常方便,极大地提升了生活的便利性。然而,随着耳夹式耳机的热度不断攀升&#…

IDEA插件开发入门

前置条件: Java17、Gradle8 1. 安装插件 Plugin DevKit 2. 新建项目 File → New → Project 3. 项目结构 📢 新建一个XxxAction 4. 插件入口 XxxAction 继承 AnAction ,实现 actionPerformed 方法,这是一个插件的入口 5. 插件配置 …

裸辞后勇闯AI领域:我的AGI产品经理之路

2021 年,我开始接触生成式 AI。2023 年 8 月,我按捺不住内心对 AI 技术浪潮的热情,从外企裸辞投身 AI。2023 年,我们团队先后入围由百度和阿里组织的 AGI 黑客马拉松活动,并在阿里第三季 AI 创客松中获得“从 0 到 1 奖…

远程唤醒局域网主机技术Wake On LAN

WOL用途 如果你在开发一个计算机管理软件(利旧现有主机,实现统一运维管理),例如电脑课开课前老师一键开启电教室的电脑 。 魔法原理 Wake-on-LAN 的原理其实很简单。我们发送一个特殊的网络包(俗称"魔术包&quo…

学生党头戴式蓝牙耳机怎么选?平价学生党头戴式蓝牙耳机排行推荐

在选择适合学生党的头戴式蓝牙耳机时,我们不仅要考虑价格因素,还要兼顾音质、舒适度、降噪效果、续航能力以及是否具备实用的功能等多个方面,那么学生党头戴式蓝牙耳机怎么选?作为学生群体,我们既追求性价比&#xff0…

微软推出GRIN-MoE:开创专家路由新范式

前沿科技速递🚀 在人工智能领域,模型的性能和可扩展性一直是研究的热点。微软最近推出的GRIN-MoE(Gradient-Informed Mixture-of-Experts)模型,以其独特的架构和显著的性能表现,正引领着AI技术的前沿&#…

南沙csp-j/s一对一家教 解一本通题: 1937:【06NOIP普及组】数列

【题目描述】 给定一个正整数k(3≤k≤15),把所有k的方幂及所有有限个互不相等的k的方幂之和构成一个递增的序列,例如,当k3时,这个序列是: 1,3,4,9,10,12,13&a…

一文读懂Ingress-Nginx以及实践攻略

一文读懂Ingress-Nginx以及实践攻略 目录 1 概念 1.1 什么是Ingress? 1.1.1 主要功能: 1.2 Ingress的组件1.3 什么是ingress-nginx1.4 ingress-nginx优点和限制1.5 版本兼容性矩阵 2 实践: Ingress nginx部署 2.1 使用helm部署ingress-ngin…

cscode搭建vue项目

创建前安装环境 ctrlj弹出终端 window需要管理员运行并且授权 node -v #显示版本号,说明 node 已经装好 npm -v #显示版本号,说明 npm 可以使用 # 安装cnpm npm install -g cnpm --registryhttps://registry.npm.taobao.org cnpm -v #显示版本号&…