代码要写成别人看不懂的样子(七)

本篇文章参考书籍《JavaScript设计模式》–张容铭

前言

  来了各位,本节整个硬活,组合模式,这个模式在应对复杂需要的时候有超高的灵活性,举个例子,乐高大家都玩过吧,各种各样的模型都能拼。乐高的拼装有个特点,一块积木上面是一个一个的小圆珠体,下面是可以衔接圆柱体的卡槽。
在这里插入图片描述
  通过不同的组合我们可以拼出很多我们像要的东西,只有想不到的需求,没有实现不了的方案。

注意事项

  使用组合模式的时候,需要有两点注意:

  1. 所有接口要统一,所有类都要继承于一个虚拟父类(这一点保证所有积木都可以拼接在一起)
  2. 叶子节点的子类不允许再进行组合(类似下图中红色区域,该子类上面不需要再组合其他东西了,所有不需要再有圆柱体了)

在这里插入图片描述

组合模式

1.同一父类

  首先要满足所有子类都继承于一个虚拟父类。下面我们创建一个关于新闻的组合模式。

var News = function() {
	//子组件容器
	this.children = [];
	//当前组件元素
	this.element = null;
}
News.prototype = {
	init: function() {
		throw new Error('重写方法');
	},
	add: function() {
		throw new Error('重写方法');
	},
	getElement: function() {
		throw new Error('重写方法');
	}
}

2.组合需要有容器

  所有子类是放在这个容器当中的。

//容器构造类函数
var Container = function(id, parent) {
	//构造函数继承父类
	News.call(this);
	//模块id
	this.id = id;
	//模块父容器
	this.init();
}
//寄生组合继承
function inheritPrototype(subType, superType){
    let prototype = Object(superType.prototype); //创建对象
    prototype.constructor = subType; //增强对象
    subType.prototype = prototype; //指定对象
}
inheritPrototype(Container, News);
//构建方法
Container.prototype.init = function() {
	this.element = document.createELement('ul');
	this.element.id = this.id;
	this.element.className = 'new-container';
}
//添加子元素方法
Container.prorotype.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);
}

  下一层级构造方式与上面类似。

//下一层级成员
var Item = function(classname) {
	News.call(this);
	this.classname = classname || '';
	this.init();
}
inheritPrototype(Item, News);
Item.prototype.init = function(child) {
	this.element = document.createElement('li');
	this.element.calssName = 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;
}
var NewsGroup = function(calssname) {
	News.call(this);
	this.classname = classname || '';
	this.init();
}
inheritPrototype(NewsGroup, News);
NewsGroup.prototype.init = function() {
	this.element = document.createElement('div');
	this.element.calssName = 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;
}

3.创建子类成员

  容器造好之后,我们要创建每一个叶子节点类

var ImageNews = function(url, href, calssname) {
	News.call(this);
	this.url = url || '';
	this.href = href || '#';
	this.calssname = calssname || '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.calssname;
	this.element.href = this.href;
}
ImageNews.prototype.add = function() {}
ImageNews.prototype.getElement = function() {
	return this.element;
}

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;
}
ImageNews.prototype.add = function() {}
ImageNews.prototype.getElement = function() {
	return this.element;
}

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;
}

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(TypeNews, 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;
}

4.创建一个完整的类

  新闻类模块创建完成了,使用时需要使用 add 方法,像一棵树一样,一层一层的创建。

var newsl = new Container('news', document.body);
newsl.add(
	new Item('normal').add(
		new IconNews('1024程序员节,你coding了嘛', '#', 'video')
	)
).add(
	new Item('normal').add(
		new IconNews('双十一,你冲了吗', '#', 'live')
	)
).add(
	new Item('normal').add(
		new NewsGroup('has-img').add(
			new ImageNews('img/1.jpg', '#', 'small')
		).add(
			new EasyNews('湖人总冠军', '#')
		)
	)
).add(
	new Item('normal').add(
		new TypeNews('詹姆斯球鞋冠军配色', '#', 'NBA', 'left')
	)
).add(
	new Item('normal').add(
		new TypeNews('易建联生日快乐', '#', 'CBA', 'Right')
	)
).show();

  创建的每一条新闻都是一个独立的个体,互不影响,避免相互间的耦合,同时也增强了组合后模块的复杂性,以后不论有什么样的需求,只要做出相应的组合就可以轻松完成。

5.表单当中更灵活

  平常我们使用组合模式最多的地方就是表单提交,这一部分因为每一项都要求独立,且不同项目的表单都有点差异,所以很适合用组合模式。

  做法和上面的一样,先创建一个基类 Base ,然后创建三个组合类 FromItem FieldsetItem Group , 还有成员类 InputItem LabelItem SpanItem TextareaItem

var form = new FromItem('FromItem', document.body);
form.add(
	new FieldsetItem('account', '账号').add(
		new Group().add(
			new LabelItem('user_name', '用户名');
		).add(
			newInputItem('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();

  组合模式刚上手有点乱,不过核心思想还是比较清晰明确的,把需求拆分成一个一个子类,让所有子类继承一个虚拟父类就可以了。之前问过一位高人,如何才能写好代码,前辈指点:“ 惟手熟尔 ”。设计模式初学的时候,一定要多用,用的多,才能记得住。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值