结构型设计模式学习笔记

    结构型设计模式关注于如何将类或对象组合成更大更复杂的结构,以简化设计。

1,外观模式

    外观模式为一组复杂的子系统接口提供一个更高级的统一接口,通过这个借口来对子系统接口的访问更容易。在JavaScript中有时也会用于对底层结构兼容性做统一封装来简化用户使用。

1.1 添加一个点击事件

    使用如下的代码添加一个点击事件时造成的问题.

document.onclick = function(e){
	e.preventDefault();
	if(e.target !== document.getElementById('myinput')){
		hidePageAlert();
	}
}
function hidePageAlert(){
	//隐藏提示框
}

    onclick事件是DOM0级事件,这种方式绑定的事件相当于为元素绑定了一个事件方法。可以使用DOM2级的addEventListener来实现,甚至在某些老版本IE中还需要用attachEvent来实现。

1.2 兼容方式

//外观模式实现
funciton addEvent(dom,type,fn){
	//对于支持DOM2级事件处理程序addEventListener方法的浏览器
	if(dom.addEventListener){
		dom.addEventListener(type,fn,false);
	//对于不支持addEventListener方法但支持attachEvent方法的浏览器
	}else if(dom.attachEvent){
		dom.attachEvent('on' + type,fn);
	//对于不支持addEventListener方法也不支持attachEvent方法,但支持on+'事件名'的浏览器
	}else{
		dom['on'+type] = fn;
	}
}

    使用这种方法就可以放心的在开发中为元素绑定事件了.如下:

var myInput = document.getElementById('myInput');
addEvent(myInput,'click',function(){
	console.log('绑定第一个事件')
});
addEvent(myInput,'click',function(){
	console.log('绑定第二个事件')
});

1.3 除此之外

    外观模式可以简化底层接口复杂性,也可以解决浏览器兼容性问题。而在第一节中写的代码除了绑定事件问题外,另外两处问题是在其他IE低版本中不兼容e.preventDefaule()和e.target。 也可以通过外观模式解决。

//获取事件对象
var getEvent = function(event){
	//标准浏览器返回event,IE下window.event
	return event || window.event;
}
//获取元素
var getTarget = function(event){
	var event = getEvent(event);
	//标准浏览器下event.target,IE下event.xrcElement
	return event.target || event.srcElement;
}
//阻止默认行为
var preventDefault = function(event){
	var event = getEvent(event);
	//标准浏览器下
	if(event.preventDefault){
		event.preventDefault();
		//IE浏览器
	}else{
		event.returnValue = false;
	}
}

   有个上面的方法,可以使用兼容的简单方式来解决上面的问题。

document.onclick = function(e){
	//阻止默认行为
	preventDefault(e);
	//获取事件源目标对象
	if(getTarget(e) !== document.getElementById('myinput')){
		hideInputSug();
	}
}

1.4,小型代码库

    外观模式可以将浏览器不兼容的方法变得简单而又兼容各个浏览器,然而这只是外观模式应用的一部分,很多代码库都是通过外观模式来封装多个功能,简化底层操作方法,比如实现获取元素的属性样式的简单方法库。

//简约版属性样式方法库
var A = {
	//通过id获取元素
	g:function(id){
		return document.getElementById(id);
	},
	//设置元素CSS属性
	CSS:function(id,key,value){
		document.getElementById(id).style[key] = value;
	},
	//设置元素的属性
	attr:function(id,key,value){
		docuemnt.getElementById(id)[key] = value;
	},
	html:function(id,html){
		document.getElementById(id).innerHTML = html;
	},
	//为元素绑定事件
	on:function(id,type,fn){
		document.getElementById(id)['on'+type] = fn;
	}
};

   "通过这个代码库,我们再操作元素的属性样式时变得更简单。"

A.css('box','background','red');  //设置CSS样式
A.attr('box','className','box');	//设置class
A.html('box','这是新添加的内容');	//设置内容
A.on('box','click',function(){		//绑定事件
	A.css('box','width','500px');
});

2 适配器模式

    适配器模式:将一个类(对象)的接口(方法或属性)转化成另外一个接口,以满足用户需求,使类(对象)之间接口的不兼容问题通过适配器得到解决。

2.1 使用适配器适配jQuery框架

    如下是自己框架的代码

//定义框架
var A = A||{};
//通过ID获取元素
A.g = function(id){
	return document.getElementById(id)
}
//为元素绑定事件
A.on = function(id,type,fn){
	//如果传递参赛是字符串则以id处理,否则以元素对象处理
	var dom = typeof id === 'string'? this.g(id) : id;
	//标准DOM2级添加事件方式
	if(dom.addEventListener){
		dom.addEventListener(type,fn,false);
	//IE DOM2级添加事件方式
	}else if(dom.attachEvent){
		dom.attachEvent('on' + type,fn);
	//简易添加事件方式
	}else{
		dom['on' + type] = fn;
	}
}

    要将该框架和jQuery框架进行适配,可以下一个如下适配器

A.g = function(id){
	//通过jQuery获取jQuery对象,然后返回第一个成员
	return $(id).get(0);
}
A.on = function(id,type,fn){
	//如果传递参赛是字符串则以id处理,否则以元素对象处理
	var dom = typeof id === 'string' ? $('#'+id):$(id);
	dom.on(type,fn);
}

2.2,参数适配器

    除此之外,适配器还有很多用途,比如使用适配器适配参数

var arr = ['vavascript','book','前端编程语言','8月8日'];

var obj = {
	name:'',
	type:'',
	title:'',
	time:''
}

function arrToObjAdapter(err){
	return{
		name:arr[0],
		type:arr[1],
		title:arr[2],
		data:arr[3]
	}
}

3 代理模式

    代理模式指由于一个对象不能直接引用另一个对象,所以需要通过代理对象在这两个对象之间起到中介的作用。

    由于用户相册模块上传的照片量越来越大,导致服务器端需要将图片上传模块重新部署到另外一个域(可理解为另一台服务器)中,这样对于前端来说,用户上传图片的请求路径发生变化,指向其他服务器,这就导致跨域问题。

   ....  先留着....  这块以后写. 

4,装饰器模式

    装饰器模式在不改变原对象的基础上,通过对其进行保证拓展(添加属性或者方法)使原有对象可以满足用户的更复杂需求。

    下面是一个对输入框添加新需求时使用装饰器模式添加的例子

    装饰器函数如下:

//装饰者
var decorator = function(input,fn){
	//获取事件源
	var input = document.getElementById(input);
	//若事件源已经绑定对象
	if(typeof input.onclick === 'function'){
		//缓存事件源原有回调函数
		var oldClickFn = input.onclick;
		//为事件源定义新的事件
		input.onclick = function(){
			//事件源原有回调函数
			oldClickFn();
			//执行事件源新增回调函数
			fn();
		}
	}else{
		//事件源未绑定事件,直接为事件源添加新增回调函数
		input.onclick = fn;
	}
	//做其他事情
}

    下面是使用装饰者装饰的用例:

//电话输入框功能装饰
decorator('tel_input',function(){
	document.getElementById('tel_demo_text').style.display = 'none';
});
//姓名输入框功能装饰
decorator('name_input',function(){
	document.getElementById('name_demo_text').style.display = 'none';
});

5,桥接模式

    桥接模式在系统沿着多个维度变化的同时,又不增加其复杂度并已达到解耦。

    使用桥接模式解耦的一个例子:

    未使用桥接前如下:

var spans = document.getElementsByTagName('span');
//为用户名绑定特效
spans[0].onmouseover = function(){
	this.style.color = 'red';
	this.style.background = '#ddd';
};
spans[0].onmouseout = function(){
	this.style.color = '#333';
	this.style.background = '#f5f5f5';
};
//为等级绑定特效
spans[1].onmouseover = function(){
	this.getElementsBuTagName('strong')[0].style.color = 'red';
	this.getElementsBuTagName('strong')[0].style.background = '#ddd';
};
spans[1].onmouseout = function(){
	this.getElementsBuTagName('strong')[0].style.color = '#333';
	this.getElementsByTagName('strong')[0].style.background = '#f5f5f5';
};

    使用桥接:

//抽象
function changeColor(dom,color,bg){
	//设置元素的字体颜色
	dom.style.color = color;
	//设置元素的背景颜色
	dom.style.background = bg;
}

var spans = document.getElementsByTagName('span');
spans[0].onmouseover = function(){
	changeColor(this,'red','#ddd');
}
spans[0].onmouseout = function(){
	changeColor(this,'#333','#f5f5f5')
}

    下面是一个使用桥接模式多元化对象的例子:

//多维变量类
//运动单元
function Speed(x,y){
	this.x = x;
	this.y = y;
}
Speed.prototype.run = function(){
	console.log('运动起来');
}
//着色单元
function Color(cl){
	this.color = cl;
}
Color.prototype.draw = function(){
	console.log('绘制色彩');
}
//变形单元
function Shape(sp){
	this.shape = sp;
}
Shape.prototype.change = function(){
	console.log('改变形状');
}
//说话单元
function Speek(wd){
	this.word = wd;
}
Speek.prototype.say = function(){
	consle.log('书写字体');
}

//球类
function Ball(x,y,c){
	//实现运动单元
	this.speed = new Speed(x,y);
	//实现着色单元
	this.color = new Color(c);
}
Ball.prototype.init = function(){
	//实现运动
	this.speed.run();
	//实现着色
	this.color.draw();
}

//人物类
function People(x,y,f){
	this.speed = new Speed(x,y);
	this.font = new Speek(f);
}

6,组合模式

    组合模式,又称部分-整体模式,将对象组合成树形结构以表示"部分整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

    一个使用组合模式的例子:

/***
 *子组件容器
 ***/
 
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("请重写你的方法");
	}
}

/***
 *容器构造类函数
 ***/
var Container = function(id,parent){
	//构造函数继承父类
	News.call(this);
	//模块id
	this.id = id;
	//模块的父容器
	this.parent = parent;
	//构建方法
	this.init();
}
//寄生式继承父类原型方法
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);
}

/***
 *行成员组合类
 ***/

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

/***
 *新闻组合体类
 ***/
 
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;
}

/***
 *创建新闻基类
 ***/

//创建图片新闻类
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;
}

//图标基类
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;
}

//文字新闻类
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;
}

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

/***
 *实例新闻模块
 ***/
var news1 = function(id){
	this.id = id;
	this.element = document.createElement('a');
	this.element.id = id;
	this.getElement = function(){
		return this.element
	}
}
 
var news1 = new Container('news',document.body);
news1.add(
		new Item('normal').add(
			new IconNews('梅西不拿金球也伟大','#','video')
		)
	).add(
		new Item('normal').add(
			new IconNews('保护强国强队用意明显','#','live')
		)
	).add(
		new Item('normal').add(
			new NewsGroup('has-imp').add(
				new ImageNews('img/1.jpg','#','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();

7,享元模式

    享元模式运用共享技术有效地支持大量的细粒度的对象,避免对象间拥有相同内容造成多余的开销。

    享元模式主要对数据,方法共享分离,讲数据和方法分成内部数据,内部方法和外部数据,外部方法。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值