JavaScript组合模式

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

大家好,欢迎来到IT知识分享网。

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(openCommand); macroCommand.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(); 

在这里插入图片描述

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/127951.html

(0)
上一篇 2025-09-03 22:26
下一篇 2025-09-03 22:33

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信