组合模式又称整体部分模式,将对象组合成树形结构以表示部分-整体的关系。客户可以统一对待组合对象和叶子对象。
组合模式中对象分为组合对象和叶子对象,组合对象包含叶子对象。叶子对象是最小的对象单元。
组合模式的使用场景
- 表示对象的部分-整体层次结构。组合模式可以方便地构造一棵树来表示对象的部分-整体结构。特别是我们在开发期间不确定这棵树到底存在多少层次的时候。在树的构造最终完成之后,只需要通过请求树的最顶层对象,便能对整棵树做统一的操作。
- 客户希望统一对待树中的所有对象。组合模式使客户可以忽略组合对象和叶对象的区别,客户在面对这棵树的时候,不用关心当前正在处理的对象是组合对象还是叶对象,也就不用写一堆if、else语句来分别处理它们。组合对象和叶对象会各自做自己正确的事情
示例:表单
表单可以很好地使用组合模式,container元素作为根对象,container下面可以有n个表单,在每个表单下面可以存在n个表单控件,每个控件下面存在label和具体的控件,这样就形成了一个树形结构。
class Container{
constructor() {
this.children = [];
this.element = null;
}
init() {
throw new Error('请重写init方法');
}
add(child) {
this.children.push(child);
this.element.appendChild(child.element);
return this;
}
}
class CreateForm extends Container{
constructor(id,method,action,parent) {
super();
this.id = id || '';
this.method = method || 'get';
this.action = action || '';
this.parent = parent || document.body;
this.init();
}
init(){
this.element = document.createElement('form');
this.element.id = this.id;
this.element.method = this.method;
this.element.action = this.action;
}
show(){
this.parent.appendChild(this.element);
}
}
class CreateLine extends Container{
constructor(className){
super();
this.className = className === undefined ? 'form-line' : 'form-line' + className;
this.init();
}
init(){
this.element = document.createElement('div');
this.element.calssName = this.className;
}
}
class CreateLabel extends Container{
constructor(text, forName){
super();
this.text = text || '';
this.forName = forName || '';
this.init();
}
init(){
this.element = document.createElement('label');
this.element.setAttribute('for', this.forName);
this.element.innerHTML = this.text;
}
}
class CreateInput extends Container {
constructor(type, id, name, defaultValue){
super();
this.type = type || '';
this.id = id || '';
this.name = name || '';
this.defaultValue = defaultValue || '';
this.init();
}
init(){
this.element = document.createElement('input');
this.element.type = this.type;
this.element.id = this.id;
this.element.name = this.name;
this.element.value = this.defaultValue;
}
add(){
throw new Error('不能添加子节点');
}
}
let form = new CreateForm('owner-form', 'get', '/aaa.html', document.body);
let userLine = new CreateLine().add(new CreateLabel('用户名', 'user')).add(new CreateInput('text', 'user', 'user'));
let pwdLine = new CreateLine().add(new CreateLabel('密码', 'pwd')).add(new CreateLabel('password', 'pwd', 'pwd')).add(new CreateInput('password', 'pwd', 'pwd'));
组合模式优点
- 组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次。它让客户端忽略了层次的差异,方便对整个层次结构进行控制。
- 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。
- 在组合模式中增加新的容器构件和叶子构件都很方便,无须对现有类库进行任何修改,符合开闭原则。
- 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案。通过叶子对象和容器对象的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。
组合模式缺点
在增加新构件时很难对容器中的构件类型进行限制。有时希望一个容器中只能有某些特定类型的对象,例如在某个文件夹中只能包含文本文件。使用组合模式时,不能依赖类型系统来施加这些约束,因为它们都来自相同的抽象层。在这种情况下,必须通过在运行时进行类型检查来实现,这个实现过程较为复杂。
|