老规矩,学习了人家的代码,我们自己也来试试。这个例子面临的需求是我们需要对已有的DOM点击事件上增加一些操作。
// 我们以前的点击事件只需要打印1 dom.onclick = function() { console.log(1); }但是我们现在的需求要求还要输出一个2,我们当然可以返回原来的代码将他改掉,但是我们也可以用装饰者模式给他添加功能:
var oldFunc = dom.onclick; // 先将老方法拿出来 dom.onclick = function() { // 重新绑定事件 oldFunc.apply(this, arguments); // 先执行老的方法 // 然后添加新的方法 console.log(2); }上述代码就扩展了dom的点击事件,但是如果需要修改的DOM元素很多,我们要一个一个的去重新绑定事件,又会有大量相似代码,我们学设计模式的目的之一就是要避免重复代码,于是我们可以将公用的绑定操作提取出来,作为一个装饰器:
var decorator = function(dom, fn) { var oldFunc = dom.onclick; if(typeof oldFunc === 'function'){ dom.onclick = function() { oldFunc.apply(this, arguments); fn(); } } } // 调用装饰器,传入参数就可以扩展了 decorator(document.getElementById('test'), function() { console.log(2); })这种方式特别适合我们引入的第三方UI组件,有些UI组件自己封装了很多功能,但是并没有暴露出接口,如果我们要添加功能,又不能直接修改他的源码,最好的方法就是这样使用装饰器模式来扩展,而且有了装饰工厂之后,我们还可以快速批量修改。
适配器模式适配器想必大家都用过,我家里的老显卡只有HDMI接口,但是显示器是DP接口,这两个插不上,怎么办呢?答案就是买个适配器,将DP接口转换为HDMI的就行了。这里的适配器模式原理类似,当我们面临接口不通用,接口参数不匹配等情况,我们可以在他外面再包一个方法,这个方法接收我们现在的名字和参数,里面调用老方法传入以前的参数形式。
基本结构适配器模式的基本结构就是下面这样,假设我们要用的打log的函数叫mylog,但是具体方法我们又想调用现成的window.console.log实现,那我们就可以给他包一层。
var mylog = (function(){ return window.console.log; })()如果觉得上面的结构太简单了,仍然不知道怎么运用,我们下面再通过一个例子来看下。
实例:框架变更了假如我们现在面临的一个问题是公司以前一直使用的A框架,但是现在决定换成jQuery了,这两个框架大部分接口是兼容的,但是部分接口不适配,我们需要解决这个问题。
// 一个修改css的接口 $.css(); // jQuery叫css A.style(); // A框架叫style // 一个绑定事件的接口 $.on(); // jQuery叫on A.bind(); // A框架叫bind当然我们全局搜索把使用的地方改掉也行,但是如果使用适配器修改可能更优雅:
// 直接把以前用的A替换成$ window.A = $; // 适配A.style A.style = function() { return $.css.apply(this, arguments); // 保持this不变 } // 适配A.bind A.bind = function() { return $.on.apply(this, arguments); }适配器就是这么简单,接口不一样,包一层改成一样就行了。
实例:参数适配适配器模式不仅仅可以像上面那样来适配接口不一致的情况,还可以用来适配参数的多样性。假如我们的一个方法需要接收一个很复杂的对象参数,比如webpack的配置,可能有很多选项,但是用户可能只用到部分,或者用户可能传入不支持的配置,那我们需要一个将用户传入的配置适配到标准配置的过程,这个做起来其实也很简单:
// func方法接收一个很复杂的config function func(config) { var defaultConfig = { name: 'hong', color: 'red', // ...... }; // 为了将用户的配置适配到标准配置,我们直接循环defaultConfig // 如果用户传入了配置,就用用户的,如果没传就用默认的 for(var item in defaultConfig) { defaultConfig[item] = config[item] || defaultConfig[item]; } } 总结高扩展性的核心其实就是高内聚,低耦合,各个模块都专注在自己的功能,尽量减少对外部的直接依赖。
职责链模式和观察者模式主要是用来降低模块间耦合的,耦合低了就可以很方便的对他们进行组织,给他们扩展功能,适配器模式和装饰器模式主要是用来在不影响原有代码的基础上进行扩展的。
如果我们需要对某个对象进行一系列的操作,这些操作可以组织成一个链条,那我们可以考虑使用职责链模式。链条上的具体任务不需要知道其他任务的存在,只专注自己的工作,消息的传递由链条负责。使用了职责链模式,链条上的任务可以很方便的增加,删除或者重新组织成新的链条,就像一个流水线一样。