面试官:说说你对组合模式的理解?应用场景?

本文介绍了组合模式的概念,展示了如何通过组合模式构建树形结构并实现部分整体的层次操作。文中以命令模式为例,说明了组合模式在命令分发和统一处理中的应用,同时提出了在实际使用中可能出现的问题及解决方案,强调了组合模式的透明性和一致性。此外,还讨论了组合模式在添加和删除树节点时的灵活性,以及如何通过组合模式实现自上而下的请求流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

a8ec7545618ae0926af19ff59e9c1e80.png

一、是什么

组合模式,又叫 “部分整体” 模式,将对象组合成树形结构,以表示 “部分-整体” 的层次结构。通过对象的多态性表现,使得用户对单个对象和组合对象的使用具有一致性

如下面的代码:

var closeDoorCommand = {
  execute: function () {
    console.log('关门');
  }
};
var openPcCommand = {
  execute: function () {
    console.log('开电脑');
  }
};
var openQQCommand = {
  execute: function () {
    console.log('登录 QQ');
  }
};

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

上述是命令模式的一个应用,macroCommand命令叫做组合对象,其包含了closeDoorCommandopenPcCommandopenQQCommand三个叶对象

8cf487721490f29e338503844c9942e5.png

macroCommandexecute 方法里,并不执行真正的操作,而是遍历它所包含的叶对象,把真正的 execute 请求委托给这些叶对象

二、应用场景

组合模式应树形结构而生,所以组合模式的使用场景就是出现树形结构的地方:

  • 「命令分发:」 只需要通过请求树的最顶层对象,便能对整棵树做统一的操作。在组合模式中增加和删除树的节点非常方便,并且符合开放-封闭原则

  • 「统一处理:」 统一对待树中的所有对象,忽略组合对象和叶对象的区别

如将上述例子稍复杂,当我们点击按钮时,出发一系列操作(打开空调,打开电视,打开音响)其中打开电视和打开音响是一组组合对象,如下代码:

<button id=button>按我</button>
<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 openAcCommend = {
    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)

  // 关门、开电脑、登QQ的命令
  var closeDoorCommand = {
    execute: function () {
      console.log('关门');
    }
  };
  var openPcCommand = {
    execute: function () {
      console.log('开电脑');
    }
  };
  var openQQCommand = {
    execute: function () {
      console.log('登录 QQ');
    }
  };
  var macroCommand2 = MacroCommand();
  macroCommand2.add(closeDoorCommand);
  macroCommand2.add(openPcCommand);
  macroCommand2.add(openQQCommand);

  // 所有命令组合成一个超级命令
  var macroCommand = MacroCommand();
  macroCommand.add(openAcCommend)
  macroCommand.add(macroCommand1)
  macroCommand.add(macroCommand2)

  // 给超级遥控器绑定命令
  var setCommand = (function (command) {
    document.getElementById('button').onclick = function () {
      command.execute()
    }
  })(macroCommand)
</script>

组合模式的透明性使得发起请求的客户不用去顾忌树中组合对象和叶对象的区别,但它们在本质上是有区别的。

组合对象可以拥有叶子节点,叶对象下面就没有子节点,所以我们可能会有一些误操作,比如试图往叶对象中添加子节点

解决方案就是给叶对象也增加 add 方法,并且在调用这个方法时,抛出一个异常来及时提醒用户,如下:

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 openAcCommend = {
  execute: function () {
    console.log('打开空调');
  },
  add: function() {
    throw new Error('叶对象不能添加子节点')
  }
}

三、总结

组合模式常使用树形方式创建对象,如下图:

ff6e6002a28f23e636f4f9efa65bbc9c.png

特点如下:

  • 表示 “部分-整体” 的层次结构,生成 "树叶型" 结构

  • 一致操作性,树叶对象对外接口保存一致(操作与数据结构一致)

  • 自上而下的的请求流向,从树对象传递给叶对象

  • 调用顶层对象,会自行遍历其下的叶对象执行

参考文献

  • https://www.runoob.com/design-pattern/composite-pattern.html

  • https://segmentfault.com/a/1190000019773556

  • https://juejin.cn/post/6995851145490989070


--The End--

系列正在更新:9/14

点击下方卡片解锁更多

75cd9805eca7de3dabc761e14ec05420.png

创作不易,星标、点赞、在看 三连支持

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值