Skip to content

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() {
}

Last update: November 9, 2024