设计模式之组合模式
1. 定义
将对象组合成树状结构以表示“部分-整体”的层次关系,使客户端统一对待单个对象和组合对象,从而简化客户端代码
2. 口语化表述
树木,在生活中很常见
中小学知识告诉我们,树木的水分,从根部吸收,通过树干,到达树叶进行生物活动(如光合作用)
树干有很多分叉,对于任何一个树干来说,需要做的就是:接收上一级树干的水分,然后分给下一级(树干或叶子)
是的,树干的职责非常清晰,就是接收上一级,然后分给下一级,它不必关心全局如何实现的,因为只要每个都这样做,就能实现最终结果
组合模式(对象树模式)的思路大致也是如此,将对象设计为一个树状结构,每个节点对象的职责相同且简单
谈到对象树,那自然会有“树”这种数据结构的优缺点,既简单(代码逻辑上)又复杂(代码设计上)
3. 源码示例
Three.js是著名的前端JavaScript 3D图形库,其基础对象是Object3D
,可参考:Object3D – three.js docs (three3d.cn)
Object3D
还有自己的Children
,依旧是Object3D
(三维模型结构大致如此,一个完整的模型通常由很多子模型构成,子模型也可能会有下一级子模型)
所以,这种对象树模式在Three.js的对象中很常见,如果要对这个Object3D
做某些操作,往往就会对每个Children
调用对应的函数,Children
会自然而然地对其Children
调用对应的函数(递归调用)
比如下面的getObjectByProperty
函数:
getObjectByProperty( name, value ) {
if ( this[ name ] === value ) return this;
for ( let i = 0, l = this.children.length; i < l; i ++ ) {
const child = this.children[ i ];
const object = child.getObjectByProperty( name, value );
if ( object !== undefined ) {
return object;
}
}
return undefined;
}
使用这种对象树模式的好处很显然,逻辑上很简单,只需要对下一级(Children)作用即可(也就是递归、或者“树”结构的优点)
4. 总结
4.1 设计优点
-
开闭原则
无需更改现有代码,就可以在原有基础中添加新节点,使其成为对象树的一部分
-
递归机制
逻辑上很简单,只需要对上一级和下一级负责
4.2 适用场景
-
需要实现树状对象结构
-
以相同方式处理简单和复杂元素
5. 参考资料
[1] 组合设计模式 (refactoringguru.cn)
[2] three.js/src/core/Object3D.js at dev · mrdoob/three.js (github.com)