js设计模式-组合模式

156 阅读2分钟

什么是宏命令

  • 宏命令是一组命令的集合,通过执行宏命令的方式,可以依次执行一批命令。

例子

const closeDoorCommand = {
  execute() {
    console.log('关门')
  }
}
const openPCCommand = {
  execute() {
    console.log('开电脑')
  }
}
const openQQCommand = {
  execute() {
    console.log('登陆QQ')
  }
}

class MacroCommand {
  constructor() {
    this.commandList = []
  }
  add(command) {
    this.commandList.push(command)
  }
  execute() {
    this.commandList.map(cmd => cmd.execute())
  }
}
// 创建宏命令
const macroCommand = new MacroCommand()
// 添加子命令
macroCommand.add(closeDoorCommand)
macroCommand.add(openPCCommand)
macroCommand.add(openQQCommand)
// 执行宏命令
macroCommand.excute()

什么是组合模式

  • 在上述宏命令例子中,macroCommand被称为组合对象,而closeDoorCommand、openPCCommand、openQQCommand都是叶对象
  • 而组合模式就是用小的对象来构建大的对象,而这些小对象本身也许是由更小的孙对象构成的。
  • 将对象组合成树形结构,以表示“部分-整体”的层次结构。
  • 并且我们只需要组合对象和单个对象拥有统一的可执行方法(execute),利用对象多态性统一对待组合对象和单个对象,使客户端忽略组合对象和单个对象的不同,在上述例子中,我们添加命令时不需要关注添加的命令是宏命令还是普通的子命令

例子

image.png

export class Folder {
  constructor(name) {
    this.name = name
    this.parent = null;
    this.files = []
  }
  add(file) {
    file.parent = this;
    this.files.push(file)
  }
  scan() {
    console.log('开始扫描文件夹'+ this.name)
    this.files.map(f => {
      f.scan()
    })
  }
  remove() {
    if (!this.parent) { // 根结点或者树外的游离节点
      return
    }
    this.parent.files.map((file, index) => {
      if (file === this) {
        this.parent.files.splice(index, 1)
      }
    })
  }
}

export class File {
  constructor(name) {
    this.name = name
    this.parent = null
  }
  add() {
    throw new Error('文件夹下不能添加问津啊')
  }
  scan() {
    console.log('开始扫描文件:' + this.name + '-' + this.parent.name)
  }
  remove() {
    if (!this.parent) { // 根结点或者树外的游离节点
      return
    }
    this.parent.files.map((file, index) => {
      if (file === this) {
        this.parent.files.splice(index, 1)
      }
    })
  }
}

// 现有的文件目录
export const folder = new Folder('学习资料')
const folder1 = new Folder('JS')
const folder2 = new Folder('JQ')

const file1 = new File('js设计模式')
const file2 = new File('精通JQ')
const file3 = new File('重构与模式')

folder1.add(file1)
folder2.add(file2)

folder.add(folder1)
folder.add(folder2)
folder.add(file3)

//移动硬盘的文件目录
const folder3 = new Folder('node.js')
const file4 = new File('深入浅出node')
folder3.add(file4)
const file5 = new File('js语言精髓')

// 将移动硬盘的文件复制到现有目录中
folder.add(folder3)
folder.add(file5)

// 扫描
folder.scan()
// 移除js语言精髓 以及 精通JQ文件夹
file5.remove()
folder2.remove()
// 移除后扫描
console.log('-------------remove---------------')
folder.scan()

结果如下 image.png

需要注意的地方

  • 组合模式不是父子关系,所以我们例子中复制是直接复制到该层文件夹
  • 对叶对象操作的一致性,组合模式除了要求组合对象和叶对象拥有相同的接口之外,还有一个必要条件,就是对一组对象的操作必须具有一致性,就是该操纵对待列表中的每个叶对象都需要是一致执行的。
  • 叶对象和组合对象之间是完全的单映射关系,不存在双向映射关系,即叶对象只和其中一个组合对象保持映射关系
  • 用职责链模式提高组合模式性能,当节点较多时,遍历树的过程中,性能方面可能不够理想,这个时候我们可以借助职责链模式去提高性能。