04-编程思想的提升—提高代码可扩展性¶
统计信息:字数 7810 阅读16分钟
代码的可扩展性:本质上就是使用不同的设计模式优化代码。程序的可扩展性和产品功能的可扩展性。
提高可扩展性的目的:面对需求变更,方便快速更改代码;减少代码修改过程的难度。
哪些设计模式可以保证代码的可扩展性¶
更改方法:装饰者模式和适配器模式
装饰者模式¶
如果一个旧方法已经写好,需要增加一个新的功能,但是我们不方便更改旧方法(其他地方可能使用旧方法)或者不能更改(第三方库提供的方法),所以采用装饰者模式。
简单的说,就是把原来的方法包装一下,增加新的功能。
装饰者模式关注的是内部的功能
function a() {
console.log('Hello');
}
function sayHelllo (name) {
a();
console.log(name);
}
// 给ajax加上一个默认的动画效果
var ajax = $.ajax;
$.ajax = function() {
ajax.call(this);
loading();
}
// 此时已经用自己写的代码覆盖了原生第三方库的ajax方法
// 下面给点击事件增加一个效果
dom.onclick = function() {
console.log(1);
}
var oldFn = dom.onclick;
dom.onclick = function() {
oldFn();
loading();
}
oldFn = null;
适配器模式¶
类似于生活中的安卓接口转换成苹果接口。
一个模块需要的方法和另一个模块提供的接口不一致。关注的是两个模块间的接口。
原来有一个A类,有一个B类,现在想要让A类具有B类的方法,但是不要更改A类内部代码。所以可以写一个适配器 Adapter类,继承自A类,并且增加了B类的方法。这样新的适配器就能满足我们的需求,同时不更改原来类。
这个适合于两个不同的类(可能是第三方的类),然后统一成一个适配器类,可以同时调用这两个类的API。如果两个模块内部完全不同,那么不适合这个设计模式。
例如我们有一个安卓充电器,有一个苹果设备需要充电。我们不能改变充电器和苹果设备,所以我们需要增加一个转接器,把安卓的接口转换成苹果的接口
例如有一个dog class,想要让狗有其他的方法,但是不能更改原始狗这个类,所以我们需要一个适配器(转接线)。
function dog() {
}
dog.ptototype.sayHello = function() {
console.log('hi');
}
function dogAdapter(dogObject) {
this.dogObject = dogObject;
}
dogAdapter.prototype = new bird();
代码上,就是设计一个新的类,这个类继承自一个类,同时有另一个类的方法(方法混入模式)。
eg2: 如果项目中使用两个框架,Jquery 和 A 框架,我们需要操作DOM和CSS,但是不同的框架的操作方法不同,所以我们需要改造一下API。
function myA() {
A.call(this);
}
myA.prototype = A.prototype;
myA.prototype.css = function() {
A.c.call(this, arguments);
}
myA.prototype.on = function() {
A.o.call(this, arguments);
}
window.$ = myA;
数据适配器:后端传过来的数据可能不满足前端的格式和要求,然后使用默认数据过滤处理一下。
function a(ob) {
// default parameters
var adapter = {
a: 1,
b: 2
};
for (let i in adapter) {
adapter[i] = ob[i] || adapter[i];
}
// 这里可以使用ES6的函数默认参数代替
}
更改需求:职责链模式+观察者模式
订阅发布模式¶
低耦合:两个独立的模块之间进行通信,一个模块订阅某个事件(对应的某个函数),另一个模块触发(发布)这个事件,就会触发前一个模块中的某个函数。这样避免了两个模块之间直接调用的耦合性。
var Observer = {
_message: {};
regist: function() {
// 订阅
},
fire: function(type) {
// 触发
var len = this._message[type].length;
for (var i = 0; i < len; i++) {
this._message[type][i].call(this);
}
},
remove: function(type) {
// 移除
var i = this._message[type].length - 1;
for (;i >= 0; i--) {
// this._message[type][i]
this._messgae[type][i] === fn && this._message[type].splice(i, 1);
}
}
}
Observer.regist('mode1sendMode2', function() {
//
});
Observer.regist('method1');
Observer.fire('method1');
// 这样可以减少两个模块的耦合。在A模块中订阅函数(回调函数),在B模块中触发事件,然后在A中触发回调函数。
// 转盘demo
var domArr = [];
var father = document.getElementById('div1');
function init() {
var dom = document.createElement('div');
dom.setAttribute('class', 'div2');
father.appendChild(dom);
domArr.push(dom);
}
for (var i = 0; i < 10; i++) {
init();
}
function run() {
var nowStop = 0;
var finiaNum = Math.floor(Math.random * 10);
var stopNum = 40 + finalNum;
var timer = setInterval(() => {
var domstop = nowStop % 10;
if (domstop == 0) {
domArr[9].setAttribute('clsss', 'div2');
} else {
domArr[domstop - 1].setAttribute('class', 'div2');
}
domArr[domstop].setAttribute('class', 'div2 divon');
if (nowStop > stopNum) {
clearInterval(timer);
}
nowStop++;
}, 100);
}
设计模式:观察者模式和职责链模式:
观察者模式¶
观察者模式适合于:当多个地方会触发一个事件,不同的模块比较多,界面交互比较复杂,那么使用观察者模块统一处理事件。
例如界面打开一个对话框,然后可能不同组件不同位置都会打开,那么这时使用观察者模式,在顶层组件中注册事件,在下层不同的组件中触发事件,就维护比较简单。
其他的代码性能指标:健壮性、可复用性、可读性;
// 下面是修改后的可扩展的代码
var time = 100;
var timer = null;
var domArr = [];
var father = document.getElementById('div1');
var nowStop = 0;
var finalNum = Math.floor(Math.random() * 10);
var stopNum = 40 + finalNum;
function init() {
function _init() {
var dom = docuemnt.createElement('div');
dom.setAttribute('class', 'div2');
father.appendChild(dom);
domArr.push(dom);
}
for (let i = 0; i < 10; i++) {
_init();
}
runner();
}
init();
function runner(dom) {
function runMode() {
if (domstop == 0) {
domArr[9].setAttribute('class', 'div2');
} else {
domArr[domstop - 1].setAttribute('class', 'div2');
}
domArr[domstop].setAttribute('class', 'div2 div-on');
}
function runControll(command) {
if (command === 'run') {
// 设定一个定时器之前,要清除定时器
clearInterval(timer);
timer = setInterval(() => {
var domstop = nowStop % 10;
if (domstop == 0 && nowStop != 0) {
Observer.fire('runOver');
}
runMode(domstop);
if (nowStop > stopNum) {
clearInterval(timer);
}
}, time);
} else if (command == 'stop') {
clearInterval(timer);
} else if (command == 'keep') {
clearInterval(timer);
var keepCounter = 0;
timer = setInterval(() => {
var domstop = keepCounter % 10;
runmode(domstop);
keepCounter++;
});
}
}
runcontroll(command);
}
观察者模式适合于复杂组件的解耦:每个组件制作自己的事情,无论增加功能还是修改功能。
职责链模式¶
职责链模式:把模块组织成一个链条(消息或者状态依次在链条上传递)
案例二:统一处理发送请求(可以处理一下现在统一发送请求的报错处理)
请求发送-请求前默认处理-自定义处理前处理-自定义处理-自定义处理后的操作
function sendMsg() {
}
function beforeSend(fn) {
}
function beforeHandle() {
}
function afterHandle() {
}