结构型设计模式关注如何把类或者对象组合成为更大、更复杂的结构,简化设计。包含:外观模式、适配器模式、代理模式、装饰者模式、桥接模式、组合模式、享元模式
外观模式(套餐服务)Facade
为一组复杂的子系统接口提供一个更高级的统一接口,通过该接口对I系统接口的访问更容易。
例如:
/**
* 外观者模式
* @param {*} dom
* @param {*} type
* @param {*} fn
*/
function addEvent(dom,type,fn){
if (dom.addEventListener) {
dom.addEventListener(type,fn,false);
}else if(dom.attachEvent) {
dom.attachEvent('on'+type,fn);
}else{
dom['on'+type] = fn;
}
}
/**
* 简单的属性样式方法库
*/
var Dom = {
getID:function(id){
return document.getElementById(id);
},
css:function(id,key,value){
document.getElementById(id).style[key]=value;
},
attr:function(id,key,value){
document.getElementById(id)[key] = value;
},
html:function(id,html){
document.getElementById(id).innerHTML = html;
},
on:function(id,type,fn){
document.getElementById(id)['on'+type] = fn;
}
};
测试代码:
<body>
<input type="text" id="box">
<p id="text">duxin</p>
<script src="js/Facede.js"></script>
<script>
var box = document.getElementById("box");
addEvent(box,'click',function(){
console.log("事件1")
});
addEvent(box,"mouseenter",function(){
console.log("事件2")
})
addEvent(box, "click", function () {
console.log("事件3")
})
Dom.css('text','color','red');
Dom.html('text','读心');
Dom.on('text','click',function(){
console.log(1111)
})
</script>
</body>
外观模式的本质是:封装交互,简化调用
适配器模式(Adapter)
为了满足用户的需求,把一个类(对象)的接口(属性或者方法)转化为另一个接口,让对象(类)之间的接口不兼容问题通过适配器得以解决。
/**
* 适配异类框架
*/
//自定义框架
var D = D || {};
D.getID = function(id){
return document.getElementById(id);
}
// //在引入JQuery替换D代码库
// D.getID = function(id){
// return $(id).get(0)
// }
//给元素绑定事件
D.on = function(id,type,fn){
//传递参数是字符串就以id处理,否则以元素对象处理
var dom = typeof id === 'string' ? this.getID(id) : id;
//DOM2级添加事件
if (dom.addEventListener) {
dom.addEventListener(type,fn,false);
}else if (dom.attachEvent) {
dom.attachEvent('on'+type,fn);
}else{
dom['on'+type] = fn;
}
}
/**
* 参数适配器,当函数需要传入多个参数时,
* 通常是以一个参数对象的方式传入
*/
function doSomeThing(obj){
var _adapter = {
name:'读心',
title:"设计模式",
age:25,
color:"blue",
size:50,
prize:25
};
for(var i in _adapter){
_adapter[i] = obj[i] || _adapter[i]
}
}
/**
* 数据适配
*/
var arr = ['javascript','book','前端编程语言','2019-4-10'];
//把arr的数据适配成对象的形式,如:
function arrToObjAdapter(arr){
return{
name:arr[0],
type:arr[1],
title:arr[2],
data:arr[3]
}
}
/**
* 服务器端数据适配
*/
function ajaxAdapter(data) {
//处理并且返回新的数据
return [data['key1'],data['key2']]
}
$.ajax({
url:"",
success:function(data,status){
if (data) {
//使用适配后的数据(返回的对象)
doSomeThing(ajaxAdapter(data))
}
}
})
<body>
<input type="text" id="box">
<p id="text">duxin</p>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="js/Facede.js"></script>
<script src="js/Adapter.js"></script>
<script>
var box = document.getElementById("box");
addEvent(box,'click',function(){
console.log("事件1")
});
addEvent(box,"mouseenter",function(){
console.log("事件2")
})
addEvent(box, "click", function () {
console.log("事件3")
})
Dom.css('text','color','red');
Dom.html('text','读心');
Dom.on('text','click',function(){
console.log(1111)
})
D.on(window,'load',function(){
D.on('box','click',function(){
console.log(165453)
})
});
var adapterData = arrToObjAdapter(arr);
console.log(adapterData)
</script>
</body>
代理模式(Proxy)
为其他对象提供一种代理,以便控制这个对象的访问。因为一个对象不能直接引用另外一个对象,而代理对象在两个对象之间起到了中介的作用。
比如用户相册模块上传照片量越大,则服务端需要把上传图片的模块重新部署到另外一个服务器(另外一个域)。那么在前端中用户上传图片的请求路径发生了变化,指向其他服务器而造成了跨域问题。
JavaScript对安全访问因素考虑,不允许跨域调用其他页面,而且还对同域名不同端口号、同域名不同协议、域名和域名对应的IP、主域名和子域名、子域名和子域名做了限制,不能直接调用。
JQuery核心代码中的代理模块
/**
* jQuery核心中的jQuery.proxy()方法
*/
//把函数绑定到上下文(context)上
var jQuery = {
proxy:function(fn,context){
if (typeof context === "string") {
var tmp = fn[context];
context = fn;
fn = tmp;
}
if (!jQuery.isFunction(fn)) {
return undefined;
}
//模拟绑定
var args = slice.call(arguments,2);
var proxy = function(){
return fn.applay(context,args.context(slice.call(arguments)));
};
proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
return proxy;
}
}
装饰者模式
促进代码复用,装饰者(Decorator)模式不太依赖创建对象的方式,更多的关注扩展对象的额外功能
/**
* Decorator 装饰者模式
* 新功能装饰构造函数
*/
//车辆构造函数
function vehicle(vehicleType){
this.vehicleType = vehicleType || 'car';
this.model = 'default';
this.license = "00000-000";
}
var test = new vehicle("car");
console.log(test);
var truck = new vehicle("truck");
//添加功能
truck.setModel = function(modelName){
this.model = modelName;
}
truck.setModel("CAT");
console.log(truck);
var test1 = new vehicle('car');
console.log(test1)
/**
* 被装饰的对象构造函数
*/
function MacBook(){
this.cost = function(){return 997;}
this.screenSize = function(){return 14;}
}
function Memory(macbook){
var v = macbook.cost();
macbook.cost = function(){
return v + 75;
};
}
function Engraving(macbook){
var v = macbook.cost();
macbook.cost = function(){
return v + 200;
};
}
function Insurance(macbook){
var v = macbook.cost();
macbook.cost = function(){
return v + 250;
}
}
var mb = new MacBook();
Memory(mb);
console.log(mb.cost());
console.log(mb.screenSize());
/**
* 接口
*/
var reminder = new Interface('List',["summary","placeOrder"]);
var properties = {
name: "duxin",
data:"2019/04/11",
actions:{
summary:function(){
return "remember"
},
placeOrder: function () {
return "Ordering "
}
}
};
function Todo(config){
Interface.ensureImplements(config.reminder);
this.name = config.name;
this.methods =config.actions;
}
//Todo实例
var todoItem = Todo(properties);
console.log(totItem.methods.summary());
桥接模式(Brideg)
在系统沿着多个维度变化时,又不能增加它的复杂度。但是页面中的一些细节的改变经常因为逻辑相似而导致代码大片臃肿。桥接模式就是把代码中相似的逻辑抽象出来。
/**
* 桥接模式
*/
function changeColor(dom,color,bg){
//设置元素的字体颜色
dom.style.color = color;
//设置元素的背景颜色
dom.style.background = bg;
}
//测试
span[0].onmouseout = function(){
changeColor(this,"#ddd","#909");
}
span[1].onmouseover =function(){
changeColor(this.getElementsByTagName("strong")[1],"#000","red")
}
/**
* 实例
*/
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 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()
}
桥接模式就是把实现层(例如页面元素的绑定事件)和抽象层(修饰页面的UI逻辑)分离,两个部分独立变化。
组合模式
就是把对象组合成为树形结构来表示“部分整体”的层次结构
如:
/**
* 寄生式继承
*/
//原型继承
function inheritObject(o) {
function F() { }
F.prototype = o;
return new F();
}
/**
* 寄生组合式继承
* 终极继承者
* 参数 subClass 子类
* 参数 superClass 父类
*/
function inheritPrototype(subClass, superClass) {
var p = inheritObject(superClass.prototype);
//防止在重写子类原型导致子类的constructor属性被修改
p.constructor = subClass;
//设置子类原型
subClass.prototype = p;
};
/**
* 组合模式
* 新闻模块的实例
*/
var News = function(){
this.children = [];
this.element = null;
}
News.prototype = {
init:function(){
console.log("暂停服务")
},
add:function(){
console.log("方法")
},
getElement:function(){
console.log("重构方法")
}
}
//容器类的构造函数
var Container = function(id,parent){
News.call(this);
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 = 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.typs = 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 new1 = new Container("news",document.body);
new1.add(
new Item("normal").add(
new IconNews("xdghdgfh","#","video")
)
).add(
new Item("normal").add(
new NewsGroup('has-img').add(
new ImageNews("http://image.baidu.com/search/detail?ct=503316480&z=undefined&tn=baiduimagedetail&ipn=d&word=%E8%AF%BB%E4%B9%A6%E4%BC%9A&step_word=&ie=utf-8&in=&cl=2&lm=-1&st=undefined&hd=undefined&latest=undefined©right=undefined&cs=1237806382,2069296067&os=2466978500,905549571&simid=3484268445,337602749&pn=2&rn=1&di=84150&ln=1887&fr=&fmq=1554998108461_R&fm=&ic=undefined&s=undefined&se=&sme=&tab=0&width=undefined&height=undefined&face=undefined&is=0,0&istype=0&ist=&jit=&bdtype=0&spn=0&pi=0&gsm=0&objurl=http%3A%2F%2Fimg.redocn.com%2Fsheji%2F20160203%2Fdushuhuiyouzhongguofenghaibao_5848794.jpg&rpstart=0&rpnum=0&adpicid=0&force=undefined","#","small")
).add(
new EasyNews("!!!!")
).add(
new EasyNews("@@@@")
)
)
).add(
new Item('normal').add(
new TypeNews(">>>","#","NBA","left")
)
).add(
new Item("normal").add(
new TypeNews("}}}}}}","#","CBA","right")
)
).show();
享元模式(Flyweight)
使用共享技术有效的支持大量的细粒度的对象,避免了对象拥有相同的内容造成多余的开销。
享元模式就是对数据、方法共享分离,将数据、方法分成内部数据、内部方法和外部数据、外部方法。
内部数据和内部方法指的是相似或者公有的数据和方法,将它们提取出来减少一些开销,提高性能。
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;
}
}
}
}();
/**
* 简单的新闻分页
*/
var paper = 0;
var num = 5;
//新闻总条数
var len = article.length;
for(var i =0;i<5;i++){
if(article[i]){
Flyweight.getDiv().inerHTML = article[i];
}
}
//下一页
document.getElementById("next_page").onclick = function(){
if(article.length < 5) return;
var n = ++paper * num % len;
var j = 0;
for(;j<5;j++){
if(article[n+j]){
Flyweight.getDiv().innerHTML = article[n+j];
}else if(article[n+j-len]){
Flyweight.getDiv().innerHTML = article[n+j-len];
}else{
Flyweight.getDiv().innerHTML = "";
}
}
}
享元模式就是为了提高程序的执行效率和系统的性能。