结构型设计模式
如何将类或对象组合成更大、更复杂的结构,简化设计
1.外观模式
1.1为一组复杂的子系统接口提供一个更高级的统一接口
通过这个接口对子系统接口的访问更容易
//添加一个点击事件
document.onclick = function(e){
e.preventDefault();
if(e.target !== document.getElementById('myinput')){
hidePageAlert();
}
}
function hidePageAlert(){
//隐藏提示框
}
为document绑定了一个onclick事件,onclick是DOM0级事件,也就是说为元素绑定一个事件方法
如果有人再次通过这种方式来绑定click,会覆盖你定义的事件方法,所以应该使用DOM2级事件addEventListener
但是老版本IE不支持这个方法,使用attachEvent,不支持DOM2级事件处理程序的浏览器,只能使用onclick
1.2兼容模式
//外观模式
function 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);
//以上都不支持
}else{
dom['on' + type] = fn;
}
}
2.适配器模式
2.1将一个类的接口转换成为另外一个接口
公司内部开发使用A框架,想引入jQuery
如果A框架和jQuery很类似,使用适配器来匹配
window.A = A = jQuery;
就可以使用jQuery来使用之前的代码
2.2适配异类框架
如果框架之间差别太大,适配起来繁琐
2.3参数适配器
//参数太多,理清顺序很麻烦
//以一个参数对象传入
function doSomeThing(obj){
var _adapter = {
name : '雨夜清荷',
title : 'xgd',
age : 24,
color : 'red',
size : 100,
};
for(var i in _adapter){
_adapter[i] = obj[i] || _adapter[i];
}
}
2.4数据适配
后端数据因为架构改变导致传递的数据结构发生变化
后端传来的数据不是我们想要的数据类型
我们可以写一个适配器,适配成我们想要的格式
3.代理模式
一个对象不能直接引用另一个对象,通过一个代理在两个对象之间起到中介的作用
3.1用户相册模块上传的照片量越来越大,导致服务端需要将图片上传模块重新部署到
另外一个域(另一个服务器),对于前端来说,路径发生改变,导致跨域
3.2因为JS中同源策略的限制,不同域名、端口号、协议,任何一个不同,就造成跨域
3.3找一个代理对象来处理两个对象的通信
3.4代理对象
1.站长统计
简单一点的,比如img标签通过src属性可以向其他域下的服务器发送请求
这类请求是get请求,并且是单向的,也就是没有响应数据
应用:
站长平台对你的页面统计项,当你的页面触发一些动作的时候
向站长平台发送类似这类img的get请求,对你的请求进行统计
然而你并不知道
2.JSONP
通过script标签,比如我们在CDN(内容分发网络,一种更接近用户的网络架构,就近获取内容)
在src中添加相应的字段信息,然后服务端相应生成一份内容
3.代理模板
不同域的调用是有限制的,那么自己域中的两个页面相互之间的调用是可以的
自己域:X域
被代理页面:A页面
1.发送请求的模块
form表单提交,负责向Y域发送请求
提供额外的两组数据:
执行的回调函数名、X域中代理模板所在的路径并将target目标指向内嵌框架
2.内嵌框架
如iframe,负责提供第一部分中form表单的响应目标target的指向
并将嵌入X域中的代理页面作为子页面
3.回调函数
处理返回来的数据
代理页面:B页面
另外的域:Y域
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';
});
5.桥接模式
系统沿着多个维度变化的同时,不增加其复杂度并已达到解耦
5.1提取共同点
对相同的逻辑做抽象提取处理,代码简洁、重用率更大、更可读
5.2事件与业务逻辑之间的桥梁
先抽象提取共用部分,将实现与抽象通过桥接方法链接在一起,达到解耦
5.3多元化对象
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(){
console.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);
}
People.prototype.init = function(){
this.speed.run();
this.font.say();
}
实现一个人物
var p = new People(10, 12, 16);
p.init();
6.组合模式
部分-整体模式,将对象组合树形结构以表示"部分整体"的层次结构
对单个对象和组合对象的使用具有一致性
6.1套餐服务
先拆分出个体,然后按需要寻找相应的个体合成新的整体的意思
6.2每个成员都有祖先
要求:接口的统一
//让所有的新闻继承至一个新闻虚拟父类News
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("请重写你的方法");
}
}
父类是虚拟父类,为何声明一些特权变量
因为后面的继承子类都要声明这两个变量,简化子类提前声明在父类中
拆分确定层次关系
最顶层是一个新闻模块的容器,再往下就是每一行新闻成员集合
每一行还可能是新闻组合体,最后一层就是新闻对象
6.3.组合要有一个容器类
//容器类构造函数
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(){
//在子元素容器中插入子元素
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(){
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;
}
6.4创建一个新闻类
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.5创建新闻模块
var news1 = new Container('news', document.body);
news1.add(
new Item('normal').add(
new IconNews('梅西', '#', 'video')
)
).add(
new Item('normal').add(
new NewsGroup('has-img'.add(
new ImageNews('img/1.jpg', '#', 'small')
).add(
new EasyNews('xx', '#')
).add(
new EasyNews('xxx', '#')
)
)
).show();
6.6应用在表单
7.享元模式
共享技术有效支持大量的细粒度的对象,避免拥有相同类容造成多余的开销
7.1新闻翻页
所有新闻具有相同结构,当几百条新闻同时插入页面并操作造成的多余开销
在低版本IE浏览器中或严重影响其性能
7.2享元模式
对其数据、方法共享分离,将数据和方法分成内部数据、内部方法、外部数据、外部方法
内部数据和内部方法指的是相似或者共有的数据和方法,需要一出来减少开销
7.3享元对象
新闻个体具有共同的结构,作为内部方法,下一页绑定的事件不能再抽象了,作为外部方法
操作方法,操作这些提取出来的内部数据
var Flyweight = function(){
//已创建的元素
var created = [];
//创建一个新闻包装容器
function create(){
var dom = document.createElement('div');
//将容器插入新闻列表容器中
document.getElementById('container').appendChild(dom);
//缓存新创建的元素
created.push(dom);
//返回创建的新元素
return dom;
}
return {
//获取创建新闻元素方法
getDiv : function(){
//已创建的元素小于当前页元素总个数,则创建
if(created.length < 5){
return create();
}else{
//获取第一个元素,并插入到最后面
var div = created.shift();
created.push(div);
return div;
}
}
}
}();
7.4实现需求
创建一个享元类,由于每页只能显示5条新闻,所以我们创建5个元素
保存在享元类的内部,通过享元类提供的方法getDiv来获取创建的元素
7.5享元动作
//横向移动和纵向移动
var FlyWeight = {
moveX : function(x){
this.x = x;
},
moveY : function(y){
this.y = y;
}
}
//其他任何角色都可以继承来实现这些方法
var Player = function(x, y, c){
this.x = x;
this.y = y;
this.color = c;
}
Player.prototype = FlyWeight;
Player.prototype.changeC = function(c){
this.color = c;
}