提供可扩展性的目的
- 面对需求变更,方便需求更改
- 减少代码修改的难度
什么是好的可扩展
- 需求的变更,不需要重写
- 代码修改不会引起大规模变动
- 方便加入新模块
低耦合
提供可扩展性的设计模式
更好的更改代码
1.适配器模式
目的:通过写一个适配器,来代替替换
应用场景:面临接口不通用的问题
2.装饰着模式
目的:不重写方法的扩展方法
应用场景:当一个方法需要扩展,但是又不好去修改方法
解耦你的方法和调用
1.命令模式
目的:解耦实现和调用,让双方互不干扰
应用场景:调用的命令充满不确定性
基本结构
适配器模式的基本结构
现在公司想用log来代替console.log
var log = (function(){
return window.console.log
})()
装饰者模式的基本结构
我们新建一个自己的方法,在其内部调用b方法,并且再执行自己的方法。这样可以再不修改原对象的情况下,扩展行为
//有一个他人写好的模块a,内部的方法b,模块为他人写好,不能修改,如何扩展b方法?
var a = {
b:function(){
}
}
function myb(){
a.b();
//要扩展的b方法
}
命令模式的基本结构
var command=(function(){
var action = {
};
return function excute(){}
})();
应用示例
适配器模式的示例
- 框架的变更
需求:目前项目使用的A框架,现在改成了B,两个框架十分类似,但是有少几个方法不同
//A.c() => $.css()
//A.o() => $.on()
window.A = $
A.c = functions(){
return $.css.apply(this,arguments);
}
A.o = functions(){
return $.on.apply(this,arguments);
}
- 参数适配
需求:为了避免参数不适配产生问题,很多框架会有一个参数适配操作
//当你的方法传入的参数较为复杂,最好都加上这么一个参数适配
function f1(config){
var _default={
name:'xxx',
color:'red',
}
for(var item in _default){
_default[item] = config[item] || _default[item]
}
}
装饰者模式的示例
- 扩展你的已有事件绑定
需求:现在项目改造,需要给input标签已经有的事件,增加一些操作
var decorater = function(dom,fn){
if(typeof dom.onclick=="function"){
var _old = dom.onclick;
dom.onclick=function(){
_old();
fn();
}
}
}
decorater(document.getElementById('dom1'),function(){
console.log(2);
})
- Vue的数组监听
需求:vue中利用defineProperty可以监听对象,那么数组怎么办?
//push,shift 阅读vue源码
var arrayproto = Array.prototype;
var arrayMethods = Object.create(arrayProto);
var methodsToPath = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
];
methodsToPath.forEach((method) => {
var original = arrayproto[method];
var result = original.apply(this,arguments);
dep.notify();
return result;
})
命令模式的示例
绘图命令
需求:封装一系列的canvas绘图命令
var canvasCommand = (function(){
var action = {
drawCircle:function(){},
drawRect:function(){},
}
return function excute(commander){
commander.forEach((item) => {
action[item.command](item.config);
})
}
})();
canvasCommand([
{command:'drawCircle',config:{}},
{command:'drawRect',config:{}},
{command:'drawCircle',config:{}},
{command:'drawCircle',config:{}},
])
绘制随机数量图片
需求:要做一个画廊,图片数量和排列顺序随机
//创建图片HTML -》展示
var createImg = (function(){
var action = {
create:function(){
var htmlArr = [];
var _htmlstring = '';
var _htmlTemplate="<div><img src='{{img-url}}' /></div><h2>{{title}}</h2>"
var displayWay = {
normal:function(arr){
return arr;
},
reverse:function(arr){
return arr.reverse();
},
}
obj.imgArr.forEach((img) => {
var _html;
_html = _htmlTemplate.replace('{{img-url}}',img.img).replace('{{img-url}}',img.title);
htmlArr.push(_html);
})
htmlArr=displayWay[obj.type](htmlArr);
_htmlstring=htmlArr.join("");
return "<div>"+_htmlstring+"</div>"
},
display:function(obj){
var _html = this.create(obj);
obj.target.innerHTML = _html;
},
}
return function excute(obj){
var _default = {
imgArr:[{img:"xxxx",title:'default title'}],
type:'normal',
target:document.body
}
for(var item in _default){
_default[item] = obj[item] || _default[item]
}
action.display(_default);
}
})();
//1.用户只管输入他要的命令,不用关心api
//2.命令与实现解耦,无论发生变动,不会彼此影响
//数据直接调用api => 数据 -> excute命令解析层 -> 调用api
课程小结
适配器模式:当面临两个新老模块间接口api不匹配,可以用适配来转化api
装饰者模式:当老的方法,不方便去直接修改,可以通过装饰者来增加功能
命令模式:解耦实现和具体命令,让实现端和命令端扩展的都更轻松