1、模式定义
组合模式,又称“部分-整体”模式,把对象组合成树形结构,以表示出“部分-整体”的层次结构。组合模式,使得用户对单个对象和组合对象的使用具有一致性。
2、生活中的组合模式
餐饮店里的套餐,是由几个独立的部分组合而成。所谓的组合模式,就把一个“整体”分割成若干个相互独立的“部分”。这样的做法,不仅可以简化“整体”的复杂性,还可以通过拓展“部分”来增强整体的丰富性。
组合模式,不仅仅是单层次组合,还可以进行多层次的组合。所谓的多层次组合,就是把“部分”看成“小的整体”,继续向下拆分出更多更小的“部分”,依次类推。
3、组合模式 举例
现在我们以新闻版块作为需求来举例。在新闻版块中,有的新闻列表有图片,有的新闻列表还有直播图标,有的新闻列表只有文字,等等。在这种情景下,我们就可以把新闻列表看成一个“整体”,把其中的图片、图标、文字等看成“部分”。最后通过组合模式,即可实现多种不同结构的新闻列表。
1)新闻基类:定义一个统一的顶级的接口父类。让所有的与新闻相关的类都继承自这个接口父类。在接口父类中,定义出必须的成员变量和方法。
var News = function() {
// 子组件容器
this.children = {};
// 当前组件元素
this.element = {};
}
News.prototype = {
init: function() {
throw new Error("请重写 init 方法");
},
add: function() {
throw new Error("请重写 add 方法");
},
getElement: function() {
throw new Error("请重写 getElement方法");
}
}
2)创建一个容器类,用于包裹所有新闻列表,代表一个新闻版块。
// 定义一个容器类
var Container = function(id, parent) {
News.call(this);
this.id = id;
this.parent = parent;
this.init();
}
// 寄生式继承:让Container类继承自 News接口类
inheritPrototype(Container, News);
Container.prototype.init = function() {
this.element = document.createElement("ul");
this.element.id = this.id;
this.element.className = "new-container";
}
Container.prototype.add = function(child) {
this.children.push(child);
this.element.appendChild(child.getElement());
return this;
}
Container.prototype.getElement = function() {
return this.element;
}
Container.prototype.show = function() {
this.parent.appendChild(this.element);
}
3)新闻列表类,代表新闻模块中的一条新闻。
var Item = function(classname) {
News.call(this);
this.classname = classname || "";
this.init();
}
inheritPrototype(Item, News);
Item.prototype.init = function() {
this.element = document.createElement("li");
this.element.className = this.classname;
}
Item.prototype.add = function(child) {
this.children.push(child);
this.element.appendChild(child.getElement());
return this;
}
Item.prototype.getElement = function() {
return this.element;
}
4)用于把多条新闻列表构成一个“组”,即对多条新闻列表进行分组。
var NewsGroup = function(classname) {
News.call(this);
this.classname = classname || '';
this.init();
}
inheritPrototype(NewsGroup, News);
NewsGroup.prototype.init = function() {
this.element = document.createElement('div');
this.element.className = this.classname;
}
NewsGroup.prototype.add = function(child) {
this.children.push(child);
this.element.appendChild(child.getElement());
return this;
}
NewsGroup.prototype.getElement = function() {
return this.element;
}
5)第一种类型的新闻列表:带有图片的新闻列表类。
var ImageNews = function(url, href, classname) {
News.call(this);
this.url = url || "";
this.href = href || "#";
this.classname = classname || "normal";
this.init();
}
inheritPrototype(ImageNews, News);
ImageNews.prototype.init = function() {
this.element = document.createElement("a");
var img = new Image();
img.src = this.url;
this.element.appendChild(img);
this.element.className = 'image-news ' + this.classname;
this.element.href = this.href;
}
ImageNews.prototype.add = function() {};
ImageNews.prototype.getElement = function() {
return this.element;
}
6)第二种类型的新闻列表:带有 ICON 图标的新闻列表类。
var IconNews = function(text, href, type) {
News.call(this);
this.text = text || "";
this.href = href || "#";
this.type = type || "video";
this.init();
}
inheritPrototype(IconNews, News);
IconNews.prototype.init = function() {
this.element = document.createElement("a");
this.element.innerHTML = this.text;
this.element.href = this.href;
this.element.className = 'icon ' + this.type;
}
IconNews.prototype.add = function() {};
IconNews.prototype.getElement = function() {
return this.element;
}
7)第三种类型的新闻列表:普通的只有文字的新闻列表类。
var EasyNews = function(text, href) {
News.call(this);
this.text = text || "";
this.href = href || "#";
this.init();
}
inheritPrototype(EasyNews, News);
EasyNews.prototype.init = function() {
this.element = document.createElement("a");
this.element.innerHTML = this.text;
this.element.href = this.href;
this.element.className = "text";
}
EasyNews.prototype.add = function() {}
EasyNews.prototype.getElement = function() {
return this.element;
}
8)第四种类型的新闻列表:带有类别标识的新闻列表类。
var TypeNews = function(text, href, type, pos) {
News.call(this);
this.text = text || "";
this.href = href || "#";
this.type = type || "";
this.pos = pos || "left";
this.init();
}
inheritPrototype(EasyNews, News);
TypeNews.prototype.init = function() {
this.element = document.createElement("a");
if (this.pos == "left") {
this.element.innerHTML = "["+ this.type +"]" + this.text;
} else {
this.element.innerHTML = this.text + "["+ this.type +"]";
}
this.element.href = this.href;
this.element.className = "text";
}
TypeNews.prototype.add = function() {}
TypeNews.prototype.getElement = function() {
return this.element;
}
9)使用上述所封装的各个“局部”组件创建一个“整体”的完整的新闻版块。
var newsBlock = new Container("news", document.body);
newsBlock.add(
new Item("normal").add(
new IconNews("梅西不拿金球也伟大", "#", "video")
)
).add(
new Item("normal").add(
new IconNews("保护强国强队用意明显", "#", "live")
)
).add(
new Item("normal").add(
new NewsGroup("has-img").add(
new ImageNews("./img/1.png", "#", "small")
).add(
new EasyNews("从240斤胖子成功变型男", "#")
).add(
new EasyNews("五大雷人跑步机", "#")
)
)
).add(
new Item("normal").add(
new TypeNews("AK47不愿为费城打球", "#", "NBA", "left")
)
).add(
new Item("normal").add(
new TypeNews("火炮飚6三分创新高", "#", "CBA", "right")
)
).show();
图例
4、组合模式 再举例
组合模式在表单应用中非常有用,把表单元素作为基本的“部分”,可以组合成多种不同结构的“整体”表单。伪代码如下:
var oForm = new FormItem('form-item', document.body);
oForm.add(
new FieldsetItem('account', '账号').add(
new Group().add(
new LabelItem('user-name', '用户名:')
).add(
new InputItem('user-name')
).add(
new SpanItem('4到6位数字或字母')
)
).add(
new Group().add(
new LabelItem('user-password', '密码:')
).add(
new InputItem('user-password')
).add(
new SpanItem('6位12位数字或者密码')
)
)
).add(
// ...
).show();
图例
5、小结
组合模式能够给我们提供一个清晰的组成结构。组合对象类通过继承同一个父类使其具有统一的方法,这样也方便了我们统一管理与使用,当然此时单体成员与组合体成员的行为表现就比较一致了。这也就模糊了简单对象与组合对象的区别。有时,这也是一种数据的分级式处理,清晰而又方便地帮助我们管理和使用数据。
当然,组合模式有时在实现需求上给我们带来更多的选择方式,虽然对于单体对象的实现简单而又单一,但是通过对其组合将会给我们带来更多的使用形式。
本章完!!
网友评论