下面是《Javascript设计模式》学习笔记七:Javascript面向对象程序设计组合模式详解的完整攻略。
什么是组合模式
组合模式是一种结构型设计模式,它通过将对象组合成树形结构来表示部分-整体的层次关系,使得用户对单个对象和组合对象的使用具有一致性。
换句话说,组合模式就是将对象组织成树形结构,以表示“部分-整体”的层次结构,并允许用户对单个对象和组合对象进行相同的操作,从而实现对整个树形结构的操作。
组合模式的结构
组合模式涉及到以下几种角色:
- 抽象类:组合中的对象声明接口,实现一些公共接口,同时也能定义一些默认行为或属性。
- 叶节点类:实现抽象节点所声明的接口,为组合中的叶节点对象提供公共接口。
- 组合节点类:组合对象,通常会存储它的子节点,实现抽象节点所声明的接口。
组合模式的实现
下面我们通过两个示例来演示组合模式的实现。
示例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技术站