《javascript设计模式》学习笔记七:Javascript面向对象程序设计组合模式详解

下面是《Javascript设计模式》学习笔记七:Javascript面向对象程序设计组合模式详解的完整攻略。

什么是组合模式

组合模式是一种结构型设计模式,它通过将对象组合成树形结构来表示部分-整体的层次关系,使得用户对单个对象和组合对象的使用具有一致性。

换句话说,组合模式就是将对象组织成树形结构,以表示“部分-整体”的层次结构,并允许用户对单个对象和组合对象进行相同的操作,从而实现对整个树形结构的操作。

组合模式的结构

组合模式涉及到以下几种角色:

  1. 抽象类:组合中的对象声明接口,实现一些公共接口,同时也能定义一些默认行为或属性。
  2. 叶节点类:实现抽象节点所声明的接口,为组合中的叶节点对象提供公共接口。
  3. 组合节点类:组合对象,通常会存储它的子节点,实现抽象节点所声明的接口。

组合模式的实现

下面我们通过两个示例来演示组合模式的实现。

示例1:餐厅菜单的实现

我们要实现一个菜单系统,分别包含面条、米饭和饮料三种类型的商品,这些商品都具有相同的基本属性,如价格、名称等。同时,我们还要实现对整个菜单的操作,如计算总价格等。

首先我们需要定义一个抽象类MenuItem,来定义各个商品的公共接口:

class MenuItem {
  constructor(name, price) {
    this.name = name;
    this.price = price;
  }
  getPrice() {
    return this.price;
  }
  getName() {
    return this.name;
  }
  getDescription() {
    throw new Error("该方法必须由子类重写");
  }
  print() {
    console.log(`${this.getName()} - ${this.getPrice()}`);
  }
}

然后,我们定义面条、米饭和饮料三个叶节点类,分别继承MenuItem类并实现各自的公共接口:

class Noodles extends MenuItem {
  constructor(name, price) {
    super(name, price);
  }
  getDescription() {
    return "面条";
  }
}

class Rice extends MenuItem {
  constructor(name, price) {
    super(name, price);
  }
  getDescription() {
    return "米饭";
  }
}

class Drink extends MenuItem {
  constructor(name, price) {
    super(name, price);
  }
  getDescription() {
    return "饮料";
  }
}

最后,我们定义菜单组合类Menu,来组合各个商品节点,并实现菜单的公共接口:

class Menu extends MenuItem {
  constructor() {
    super("菜单", 0);
    this.menuItems = [];
  }
  add(item) {
    this.menuItems.push(item);
  }
  remove(item) {
    const index = this.menuItems.indexOf(item);
    this.menuItems.splice(index, 1);
  }
  getPrice() {
    return this.menuItems.reduce((prev, item) => {
      return prev + item.getPrice();
    }, 0);
  }
  getDescription() {
    return "菜单";
  }
  print() {
    console.log(`------ ${this.getName()} -------`);

    this.menuItems.forEach((item) => {
      item.print();
    });
  }
}

这样,我们就可以创建各种商品节点,并使用菜单组合类将它们组合成为一个树形结构:

const noodles1 = new Noodles("羊肉面", 15);
const noodles2 = new Noodles("牛肉面", 12);
const rice1 = new Rice("鸡蛋炒饭", 11);
const drink1 = new Drink("可乐", 5);
const drink2 = new Drink("奶茶", 8);

const menu = new Menu();
menu.add(noodles1);
menu.add(noodles2);
menu.add(rice1);

const drinkMenu = new Menu();
drinkMenu.add(drink1);
drinkMenu.add(drink2);

menu.add(drinkMenu);

menu.print();  // 输出所有商品的名称和价格
console.log("总价格:", menu.getPrice());  // 输出所有商品的总价格

输出结果如下:

------ 菜单 -------
羊肉面 - 15
牛肉面 - 12
鸡蛋炒饭 - 11
------ 菜单 -------
可乐 - 5
奶茶 - 8
总价格: 51

示例2:文件系统的实现

我们要实现一个文件系统,其中包括了文件和文件夹两种节点,文件节点和文件夹节点具有相同的基本属性,如名称、类型等。同时,我们还要实现对整个文件系统的操作,如计算文件总数等。

同样地,我们需要定义一个抽象类Node,来定义各个节点的公共接口:

class Node {
  constructor(name, type) {
    this.name = name;
    this.type = type;
  }
  getName() {
    return this.name;
  }
  getType() {
    return this.type;
  }
  getSize() {
    throw new Error("该方法必须由子类重写");
  }
  addChild() {
    throw new Error("该方法必须由子类重写");
  }
  removeChild() {
    throw new Error("该方法必须由子类重写");
  }
  getChild() {
    throw new Error("该方法必须由子类重写");
  }
  getChildren() {
    throw new Error("该方法必须由子类重写");
  }
}

然后,我们定义文件节点和文件夹节点两个叶节点类,分别继承Node类并实现各自的公共接口:

class File extends Node {
  constructor(name, size) {
    super(name, "文件");
    this.size = size;
  }
  getSize() {
    return this.size;
  }
}

class Folder extends Node {
  constructor(name) {
    super(name, "文件夹");
    this.children = [];
  }
  addChild(node) {
    this.children.push(node);
  }
  removeChild(node) {
    const index = this.children.indexOf(node);
    if (index !== -1) {
      this.children.splice(index, 1);
    }
  }
  getChild(index) {
    return this.children[index];
  }
  getChildren() {
    return this.children;
  }
  getSize() {
    return this.children.reduce((prev, child) => {
      return prev + child.getSize();
    }, 0);
  }
}

最后,我们定义文件系统组合类FileSystem,将文件和文件夹节点都组合起来,并实现对整个文件系统的操作的公共接口:

class FileSystem extends Folder {
  constructor(name) {
    super(name);
  }
  getFileCount() {
    return this.getChildren().filter((child) => {
      return child.getType() === "文件";
    }).length;
  }
  getFolderCount() {
    return this.getChildren().filter((child) => {
      return child.getType() === "文件夹";
    }).length;
  }
  getSize() {
    return this.children.reduce((prev, child) => {
      return prev + child.getSize();
    }, 0);
  }
}

这样,我们就可以创建各种文件和文件夹节点,并使用文件系统组合类将它们组合成为一个树形结构:

const folder1 = new Folder("目录1");
const folder2 = new Folder("目录2");
const folder3 = new Folder("目录3");

const file1 = new File("文件1", 50);
const file2 = new File("文件2", 20);
const file3 = new File("文件3", 10);

folder1.addChild(file1);
folder1.addChild(folder2);

folder2.addChild(file2);
folder2.addChild(folder3);

folder3.addChild(file3);

const fs = new FileSystem("根目录");
fs.addChild(folder1);

console.log("文件总数:", fs.getFileCount());
console.log("文件夹总数:", fs.getFolderCount());
console.log("文件系统总大小:", fs.getSize());

输出结果如下:

文件总数: 3
文件夹总数: 3
文件系统总大小: 80

总结

组合模式是一种结构型设计模式,可以方便地组织对象成树形结构,实现“部分-整体”的操作。通过两个示例,我们演示了组合模式的实现方法,并学习了其中的一些经验与技巧。这些技巧包括:抽象类的使用、叶节点类的实现、组合节点类的实现等。掌握了这些技巧,我们就可以快速地实现复杂的树形结构操作。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:《javascript设计模式》学习笔记七:Javascript面向对象程序设计组合模式详解 - Python技术站

(0)
上一篇 2023年5月26日
下一篇 2023年5月26日

相关文章

  • Java代码混淆的作用是什么?

    Java代码混淆是一种将Java源码中的可读性相关信息和关键词进行随机映射和替换,以达到混淆恶意软件的源代码、减少程序被破解或反编译的效果。 使用Java代码混淆有以下几个重要作用: 防止程序被破解:通过混淆Java代码,可以抵御逆向工程等高级攻击技术,降低程序被破解的风险。 保证商业利益:商业软件一旦被破解,会带来严重的经济损失。Java代码混淆可以使破解…

    Java 2023年5月11日
    00
  • 使用Java生成jpg与压缩图片为jpg文件的代码示例

    以下是关于使用Java生成jpg并压缩图片为jpg的完整攻略。 1.使用Java生成jpg 要使用Java生成jpg图片,需要使用第三方库——JFreeChart,它可以用于绘制多种类型的图表和图形,其中包括图片。 步骤 引入JFreeChart库: xml <dependency> <groupId>jfree</groupI…

    Java 2023年5月20日
    00
  • 微信小程序实时聊天WebSocket

    下面为您详细讲解“微信小程序实时聊天WebSocket”的完整攻略。 一、前期准备 了解WebSocket协议的基础知识,包括握手过程、消息格式等; 了解微信小程序基础知识,包括小程序开发、页面结构、组件等; 确保开发环境已经安装好,包括微信web开发者工具、编辑器等。 二、创建WebSocket连接 微信小程序提供了wx.connectSocket() A…

    Java 2023年5月23日
    00
  • MybatisPlus特殊查询的实现介绍

    MybatisPlus特殊查询的实现介绍 MybatisPlus是基于Mybatis的一个增强库,其中包括了很多常用操作的封装,极大地降低了开发者的编码难度和时间成本,同时也提高了代码的可读性和可维护性。 但是有些时候,简单的CRUD操作并不能满足我们的需求。MybatisPlus提供了很多特殊查询的实现方式,本文将简单介绍其中的几种。 前置条件 在具体实现…

    Java 2023年5月20日
    00
  • PHP中auto_prepend_file与auto_append_file用法实例分析

    PHP中auto_prepend_file与auto_append_file用法实例分析 在PHP中,auto_prepend_file和auto_append_file是两个特殊的配置选项,它们分别用于在PHP脚本执行前和执行后自动执行指定的PHP脚本文件。这两个配置选项通常被用于实现一些公共功能或初始化操作。本文将详细讲解auto_prepend_fil…

    Java 2023年6月15日
    00
  • 基于Java实现计数排序,桶排序和基数排序

    基于Java实现计数排序、桶排序和基数排序 计数排序(Counting Sort) 计数排序是一种稳定的排序算法,它使用一个计数数组来记录每个元素出现的次数,并按照次数将元素依次输出到结果数组中。 步骤 初始化一个大小为 max_value 的空计数数组 遍历待排序数组,将每个元素出现的次数加入计数数组对应下标中 遍历计数数组,累加每个元素之前出现的次数,得…

    Java 2023年5月19日
    00
  • java如何将一个float型数的整数部分和小数分别输出显示

    要将一个float型数的整数部分和小数部分分别输出显示,可以使用Java中的数学函数和字符串格式化。下面是完整的攻略。 步骤一:获取float型数的整数部分和小数部分 获取float型数的整数部分可以使用Java中的Math.floor()函数,该函数将返回小于或等于输入值的最大整数。获取float型数的小数部分可以将float数减去它的整数部分,得到的结果…

    Java 2023年5月26日
    00
  • java压缩多个文件并且返回流示例

    下面为你详细讲解如何使用Java压缩多个文件并返回流,包含两条示例。 一、使用Java压缩多个文件 首先,我们需要使用Java提供的ZipOutputStream类来压缩多个文件。以下是一个示例代码: public static void compressFiles(List<File> files, OutputStream outputStr…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部