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

yizhihongxing

下面是《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中Jackson快速入门

    Java中Jackson快速入门 1. 什么是Jackson? Jackson是Java中最常用的处理JSON数据的库之一,它可以将JSON字符串转换为Java对象,或将Java对象转换为JSON字符串。 2. Jackson的使用方法 2.1 添加依赖 在Maven项目中,我们可以通过以下方式添加Jackson的依赖: <dependency>…

    Java 2023年5月19日
    00
  • java随机字符串生成示例

    当我们需要在Java应用程序中生成随机字符串时,可以使用Java中的Random类来实现。下面是一个完整的Java随机字符串生成示例攻略: 1. 导入Random类 import java.util.Random; 2. 定义随机生成字符串的方法 public static String generateRandomString(int length) { …

    Java 2023年5月26日
    00
  • Java 三种进制的数值常量操作

    Java 三种进制的数值常量操作 在Java中,数值型常量支持三种进制表示方式:十进制、八进制和十六进制。这些常量可以用于表示不同的数字大小和格式,本文将对它们进行详细的讲解。 十进制整数 十进制整数(Decimal Integer)是以10为基数的整数,常用于日常生活中的计数,例如1、2、3、10、100等等。 十进制整数的表示方法非常简单,只要直接写下数…

    Java 2023年5月26日
    00
  • java程序运行时内存分配详解

    Java程序运行时内存分配详解 Java程序运行时内存分配是Java程序员必须理解和掌握的重要知识点之一。了解内存分配可以帮助我们更好的管理内存,提高程序性能,避免程序崩溃等问题的出现。 Java内存分区 在Java中,内存分配主要分为以下几个区域: 程序计数器(Program Counter Register) 虚拟机栈(Java Virtual Mach…

    Java 2023年5月23日
    00
  • 反编译jar实现的三种方式

    好的。下面我将详细讲解“反编译jar实现的三种方式”的完整攻略。 1. 反编译jar实现的三种方式 1.1 命令行反编译 命令行反编译是最常见的反编译jar的方式,也是最简单的一种方式。主要通过利用javap命令对jar包进行操作,实现对jar包里面的class和method的反编译。 首先,打开终端,进入到jar包所在的目录。 然后,输入以下命令进行反编译…

    Java 2023年5月26日
    00
  • PHP中实现Bloom Filter算法

    下面是完整的“PHP中实现Bloom Filter算法”的攻略。 什么是Bloom Filter算法? Bloom Filter是一种可以高效地判断一个元素是否存在于一个集合中的算法。它通常用于需要快速查找某个元素的场景。 Bloom Filter实现的关键在于利用多个哈希函数对输入的元素进行哈希,从而在一个位图中将这个元素对应的位置标记为1。使用Bloom…

    Java 2023年5月19日
    00
  • SpringBoot创建并简单使用的实现

    下面我会给出一份详细的Spring Boot创建并简单使用的攻略,包含以下几个部分: 环境准备 创建Spring Boot项目 使用Spring Boot 示例1:创建RESTful API 示例2:集成Thymeleaf模板引擎 环境准备 在开始创建Spring Boot项目之前,需要进行相关的环境准备。首先,需要确保你已经安装了Java和Maven,在安…

    Java 2023年5月15日
    00
  • Java中数字相关的类有哪些?Nuber数字类和Math数学类详解

    前言 我们在解决实际问题时,会经常对数字、日期和系统设置进行处理,比如在我们的代码中,经常会遇到一些数字&数学问题、随机数问题、日期问题和系统设置问题等。 为了解决这些问题,Java给我们提供了多个处理相关问题的类,比如Number类、Math类、Random类、BigInteger类、BigDecimal类、Date类、System类等。 接下来我…

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