JavaScript组合模式

JavaScript组合模式

  • 1 什么是组合模式
  • 2 宏命令
  • 3 示例:扫描文件夹
  • 4 引用父对象

1 什么是组合模式

组合模式是一种结构型设计模式,用于将对象组合成树形结构,并使客户端能够统一处理单个对象和组合对象。它通过使用继承和组合两个概念,允许我们以递归方式构建对象树。

在组合模式中,有两种类型的对象:基本对象组合对象。基本对象是单独的、不可拆分的对象,而组合对象是由多个基本对象组合而成的对象。

组合模式将对象组合成树形结构,以表示“部分—整体”的层次结构。 除了用来表示树形结构之外,组合模式的另一个好处是通过对象的多态性表现,使得用户对单个对象和组合对象的使用具有一致性。

2 宏命令

宏命令对象包含了一组具体的子命令对象,不管是宏命令对象,还是子命令对象,都有一个execute方法负责执行命令,例如家里有一个万能遥控器,每天回家的时候,只要按一个特别的按钮,它就会帮我们关上房间门,顺便打开电脑并登录游戏。

var closeDoorCommand = {execute: function () {console.log("关门");},
};
var openPcCommand = {execute: function () {console.log("开电脑");},
};
var openGameCommand = {execute: function () {console.log("打开游戏");},
};var MacroCommand = function () {return {commandsList: [],add: function (command) {this.commandsList.push(command);},execute: function () {for (var i = 0, command; (command = this.commandsList[i++]); ) {command.execute();}},};
};var macroCommand = MacroCommand();macroCommand.add(closeDoorCommand);
macroCommand.add(openPcCommand);
macroCommand.add(openQQCommand);
macroCommand.execute();

在上面这段代码中,我们发现,宏命令中包含了一组子命令,它们组成了一个如下所示的树形结构。
在这里插入图片描述
其中,marcoCommand被称为组合对象,closeDoorCommandopenPcCommandopenGameCommand都是叶对象。在macroCommandexecute方法里,并不执行真正的操作,而是遍历它所包含的叶对象,把真正的execute请求委托给这些叶对象。

macroCommand表现得像一个命令,但它实际上只是一组真正命令的“代理”。并非真正的代理,虽然结构上相似,但macroCommand只负责传递请求给叶对象,它的目的不在于控制对叶对象的访问。

如果我们需要一个“超级万能遥控器”,可以控制家里所有的电器,这个遥控器拥有以下功能:

  • 打开空调
  • 打开电视和音响
  • 关门、开电脑、登录游戏

首先在节点中放置一个按钮button来表示这个超级万能遥控器,超级万能遥控器上安装了一个宏命令,当执行这个宏命令时,会依次遍历执行它所包含的子命令,代码如下:

<button id="button">点击一下</button>
<script>// -------- 绑定超级命令 -----------var button = document.getElementById("button");button.onclick = function () {macroCommand.execute();};
</script>
var MacroCommand = function () {return {commandsList: [],add: function (command) {this.commandsList.push(command);},execute: function () {for (var i = 0, command; (command = this.commandsList[i++]); ) {command.execute();}},};
};var openAcCommand = {execute: function () {console.log("打开空调");},
};// -------- 打开电视和打开音响命令组合 -----------
var openTvCommand = {execute: function () {console.log("打开电视");},
};var openSoundCommand = {execute: function () {console.log("打开音响");},
};var macroCommand1 = MacroCommand();
macroCommand1.add(openTvCommand);
macroCommand1.add(openSoundCommand);// -------- 关门、打开电脑和打开游戏命令组合 -----------
var closeDoorCommand = {execute: function () {console.log("关门");},
};
var openPcCommand = {execute: function () {console.log("开电脑");},
};var openGameCommand = {execute: function () {console.log("打开游戏");},
};var macroCommand2 = MacroCommand();
macroCommand2.add(closeDoorCommand);
macroCommand2.add(openPcCommand);
macroCommand2.add(openGameCommand);// -------- 组合超级命令 -----------
var macroCommand = new MacroCommand();
macroCommand.add(openAcCommand);
macroCommand.add(macroCommand1);
macroCommand.add(macroCommand2);

在这里插入图片描述
从这个例子中可以看到,基本对象可以被组合成更复杂的组合对象,组合对象又可以被组合,这样不断递归下去,这棵树的结构可以支持任意多的复杂度。在树最终被构造完成之后,让整颗树最终运转起来的步骤非常简单,只需要调用最上层对象的execute方法。每当对最上层的对象进行一次请求时,实际上是在对整个树进行深度优先的搜索,而创建组合对象的人并不关心这些内在的细节,往这棵树里面添加一些新的节点对象是非常容易的事情。

3 示例:扫描文件夹

文件夹和文件之间的关系,非常适合用组合模式来描述。文件夹里既可以包含文件,又可以包含其他文件夹,最终可能组合成一棵树。

首先分别定义好文件夹Folder和文件File这两个类。见如下代码:

/******************************* Folder ******************************/
var Folder = function (name) {this.name = name;this.files = [];
};Folder.prototype.add = function (file) {this.files.push(file);
};Folder.prototype.scan = function () {console.log("开始扫描文件夹:" + this.name);for (let i = 0, file, files = this.files; (file = files[i++]); ) {file.scan();}
};/******************************* File ******************************/
var File = function (name) {this.name = name;
};File.prototype.add = function () {throw new Error("文件下面不能再添加文件");
};File.prototype.scan = function () {console.log("开始扫描文件: " + this.name);
};

接下来创建一些文件夹和文件对象, 并且让它们组合成一棵树,这棵树就是我们D盘里的现有文件目录结构:

var folder = new Folder("学习资料");
var folder1 = new Folder("JavaScript");
var folder2 = new Folder("jQuery");
var file1 = new File("JavaScript 设计模式与开发实践");
var file2 = new File("精通 jQuery");
var file3 = new File("重构与模式");
folder1.add(file1);
folder2.add(file2);
folder.add(folder1);
folder.add(folder2);
folder.add(file3);

在这里插入图片描述
现在的需求是把移动硬盘里的文件和文件夹都复制到这棵树中,假设我们已经得到了这些文件对象:

var folder3 = new Folder("Nodejs");
var file4 = new File("深入浅出 Node.js");
folder3.add(file4);
var file5 = new File("JavaScript 语言精髓与编程实践");

接下来就是把这些文件都添加到原有的树中:

folder.add(folder3);
folder.add(file5);

在这里插入图片描述
运用了组合模式之后,扫描整个文件夹的操作也是轻而易举的,我们只需要操作树的最顶端对象:

folder.scan();

在这里插入图片描述

4 引用父对象

组合对象保存了它下面的子节点的引用,这是组合模式的特点,此时树结构是从上至下的。但有时候我们需要在子节点上保持对父节点的引用,比如在组合模式中使用职责链时,有可能需要让请求从子节点往父节点上冒泡传递。还有当我们删除某个文件的时候,实际上是从这个文件所在的上层文件夹中删除该文件的。

现在来改写扫描文件夹的代码,使得在扫描整个文件夹之前,我们可以先移除某一个具体的文件。

首先改写Folder类和File类,在这两个类的构造函数中,增加this.parent属性,并且在调用add方法的时候,正确设置文件或者文件夹的父节点:

/******************************* Folder ******************************/
var Folder = function (name) {this.name = name;this.files = [];this.parent = null;
};Folder.prototype.add = function (file) {file.parent = this; // 设置父对象this.files.push(file);
};Folder.prototype.scan = function () {console.log("开始扫描文件夹:" + this.name);for (let i = 0, file, files = this.files; (file = files[i++]); ) {file.scan();}
};

接下来增加Folder.prototype.remove方法,表示移除该文件夹:

Folder.prototype.remove = function () {if (!this.parent) return;for (let files = this.parent.files, l = files.length - 1; l >= 0; l--) {let file = files[l];if (file === this) {files.splice(l, 1);}}
};

File类的实现基本一致:

/******************************* File ******************************/
var File = function (name) {this.name = name;this.parent = null;
};File.prototype.add = function () {throw new Error("文件下面不能再添加文件");
};File.prototype.scan = function () {console.log("开始扫描文件: " + this.name);
};File.prototype.remove = function () {if (!this.parent) return;for (let files = this.parent.files, l = files.length - 1; l >= 0; l--) {let file = files[l];if (file === this) {files.splice(l, 1);}}
};

下面测试一下我们的移除文件功能:

var folder = new Folder("学习资料");
var folder1 = new Folder("JavaScript");
var file1 = new File("深入浅出 Node.js");
var file2 = new File("JavaScript 设计模式与开发实践");
folder1.add(file2);
folder.add(folder1);
folder.add(file1);folder1.remove(); // 移除文件夹
folder.scan();

在这里插入图片描述

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

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

相关文章

【MySQL】C语言连接数据库

文章目录 一、安装 MySQL 库二、MySQL C API 相关接口1、C API 官方文档2、初始化 MYSQL3、连接 MySQL4、下发 mysql 指令5、获取 mysql 查询结果6、释放 MYSQL_RES 对象7、关闭 MySQL 连接8、MySQL 其他操作9、总结 三、使用图形化工具连接 MySQL 一、安装 MySQL 库 我们之前…

nlp之文本转向量

文章目录 代码代码解读 代码 from tensorflow.keras.preprocessing.text import Tokenizer # 标记器(每一个词&#xff0c;以我们的数值做映射&#xff0c;)words [LaoWang has a Wechat account., He is not a nice person., Be careful.] # 把这句话中每一个单词&#xf…

python文件检索模拟find命令

#path代表待搜索的目录路径&#xff0c;result存储搜索到的文件路径列表 #函数将path目录中的全部子目录和文件找到保存至result def search_dir(path,result): #使用os中的listdir得到path下的目录和文件&#xff0c;保存到child_files child_files os.listdir(path)…

JavaSE19——file文件类

file文件类 在 Java File 类是 java.io 包中唯一代表磁盘文件本身的对象 File 类不能访问文件内容本身&#xff0c;如果需要访问文件内容本身&#xff0c;则需要使用输入/输出流。 File(String path)&#xff1a;如果 path 是实际存在的路径&#xff0c;则该 File 对象表示的…

java try throw exception finally 遇上 return break continue造成异常丢失

如下所示&#xff0c;是一个java笔试题&#xff0c;考察的是抛出异常之后&#xff0c;程序运行结果&#xff0c;但是这里抛出异常&#xff0c;并没有捕获异常&#xff0c;而是通过finally来进行了流程控制处理。 package com.xxx.test;public class ExceptionFlow {public sta…

【Python机器学习】零基础掌握SkewedChi2Sampler内核近似特征

有没有遇到这样的困扰:即使在拥有大量数据的条件下,传统的机器学习模型表现依然不佳?这时,数据预处理和特征工程成了解决问题的关键步骤。那么,有没有一种算法能够优化特征,提升模型性能呢? 假设一个在线商城希望通过用户行为(比如点击、购买等)来预测用户是否会成为…

toluaframework中C#怎么调用Lua的方法以及无GC方法

toluaframework中C#怎么调用Lua的方法 问题Util.CallMethodLuaManager.CallFunctionLuaFunction.LazyCall 解决方案LuaFunction脚本无GC消耗的调用 用法总结 问题 用过luaframework框架的人应该都知道框架提供了Util的工具类&#xff0c;工具类提供了一个方法就是Util.CallMet…

web:[网鼎杯 2020 青龙组]AreUSerialz

题目 点进题目发现 需要进行代码审计 function __destruct() {if($this->op "2")$this->op "1";$this->content "";$this->process();}这里有__destruct()函数&#xff0c;在对象销毁时自动调用&#xff0c;根据$op属性的值进行…

lua移植及使用

编译环境&#xff1a;Ubuntu16.04 64位 交叉编译工具&#xff1a;arm-hisiv500-linux-gcc 文章目录 1. 项目背景2. lua开源版本选择3. 封装代码3.1 源码简介3.2 封装类3.2.1 头文件3.2.2 类的实现3.3.3 sample代码 1. 项目背景 使用lua脚本&#xff0c;读取key对应的值&#x…

数字化转型系列主题:SWOT分析方法与样例说明

定义 SWOT分析是一种常用的战略性管理工具&#xff0c;用于评估一个组织、项目或个人的优势、劣势、机会和威胁。SWOT代表Strengths&#xff08;优势&#xff09;、Weaknesses&#xff08;劣势&#xff09;、Opportunities&#xff08;机会&#xff09;和Threats&#xff08;威…

【每日一题】H 指数 II

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;二分查找 写在最后 Tag 【二分查找】【数组】【2023-10-30】 题目来源 275. H 指数 II 题目解读 本题与 274. H 指数 题目一致&#xff0c;只是加强了一下条件&#xff0c;数组是有序的。 解题思路 方法一&#xff…

WLAN的组网架构和工作原理

目录 WLAN的组网架构 FAT AP架构 AC FIT AP架构 敏捷分布式AP 下一代园区网络&#xff1a;智简园区&#xff08;大中型园区网络&#xff09; WLAN工作原理 WLAN工作流程 1.AP上线 &#xff08;1&#xff09;AP获取IP地址&#xff1b; &#xff08;2&#xff09;AP发…

当『后设学习』碰上『工程学思维』

只要我成为一个废物&#xff0c;就没人能够利用我&#xff01; 雷猴啊&#xff0c;我是一只临期程序猿。打过几年工&#xff0c;写过几行代码。但今天我不想聊代码&#xff0c;我们聊聊学习这件事。 技术年年更新&#xff0c;尤其是前端框架&#xff0c;很多时候觉得学习速度都…

MyBatis的增删改查

2023.10.29 本章学习MyBatis的基本crud操作。 insert java程序如下&#xff1a; ①使用map集合传参 Testpublic void testInsertCar(){SqlSession sqlSession SqlSessionUtil.openSession();//先将数据放到Map集合中&#xff0c;在sql语句中使用 #{map集合的key} 来完成传…

JVM进阶(3)

一)什么是垃圾&#xff1f; 垃圾指的是在应用程序中没有任何指针指向的对象&#xff0c;这个对象就是需要被回收的垃圾&#xff0c;如果不及时的针对内存中的垃圾进行清理&#xff0c;那么这些垃圾对象所占用的内存空间可能一直保留到应用程序结束&#xff0c;被保留的空间无法…

Python selenium交互

视频版教程&#xff1a;一天掌握python爬虫【基础篇】 涵盖 requests、beautifulsoup、selenium selenium可以模拟用户点击事件&#xff0c;以及控制浏览器前进&#xff0c;后退等操作。 下面是一个模拟百度搜索&#xff0c;点击下一页&#xff0c;控制浏览器后退&#xff0c…

Python练习

定义学员信息类&#xff0c;包含姓名、成绩属性&#xff0c;定义成绩打印方法 (90分及以上显示优秀&#xff0c;80分及以上显示良好&#xff0c;70分及以上显示中等&#xff0c;60分及以上显示合格&#xff0c;60分以下显示不及格) class Student:def __init__(self, name, sco…

SpringMVC学习

一、SpringMvc 概述 1.什么是SpringMVC? ( •̀ ω •́ )✧&#xff1a; SpringMVC是基于MVC开发模式的框架&#xff0c;具备IOC和AOP MyBatis用来优化持久层&#xff0c; SpringMVC优化控制器。 spring是用来整合这两个框架 的框架

人工智能基础_机器学习008_使用正规方程_损失函数进行计算_一元一次和二元一次方程演示_sklearn线性回归演示---人工智能工作笔记0048

自然界很多都是正态分布的,身高,年龄,体重...但是财富不是. 然后我们来看一下这个y = wx+b 线性回归方程. 然后我们用上面的代码演示. 可以看到首先import numpy as np 导入numby 数据计算库 import matplotlib.pyplot as plt 然后导入图形画的库 然后: X = np.linspace(0,…

【LeetCode刷题-哈希】--349.两个数组的交集

349.两个数组的交集 class Solution {public int[] intersection(int[] nums1, int[] nums2) {Set<Integer> num1set new HashSet<>();Set<Integer> interset new HashSet<>();for(Integer n : nums1){ //将nums1中的元素加到num1set中num1set.add(…