《JavaScript设计模式与开发实践》原则篇(3)—— 开放-封闭原则

在面向对象的程序设计中,开放封闭原则(OCP)是最重要的一条原则。不少时候,一个程序具备良好的设计,每每说明它是符合开放封闭原则的。 当须要改变一个程序的功能或者给这个程序增长新功能的时候,可使用增长代码的方式,可是不容许改动程序的源代码。设计模式

故事背景

假设咱们是一个大型 Web 项目的维护人员,在接手这个项目时,发现它已经拥有10万行以上的JavaScript代码和数百个 JS 文件。 不久后接到了一个新的需求,即在 window.onload 函数中打印出页面中的全部节点数量。这 固然难不倒咱们了。因而咱们打开文本编辑器,搜索window.onload函数在文件中的位置,在函数内部添加如下代码bash

window.onload = function(){ 
    // 原有代码略
    console.log( document.getElementsByTagName( '*' ).length ); 
};
复制代码

应用OCP原则

Function.prototype.after = function( afterfn ){ 
    var __self = this;
    return function(){
        var ret = __self.apply( this, arguments ); 
        afterfn.apply( this, arguments );
        return ret;
    } 
};
window.onload = ( window.onload || function(){} ).after(function(){ 
    console.log( document.getElementsByTagName( '*' ).length );
});
复制代码

经过动态装饰函数的方式,咱们彻底不用理会从前 window.onload 函数的内部实现,就算拿到的是一份混淆压缩过的代码也没有关系。只要它从前是个稳定运行的函数,那么之后也不会由于咱们的新增需求而产生错误。新增的代码和原有的代码能够互不影响。app

编写符合OCP代码的方法

过多的条件分支语句是形成程序违反开放封闭原则的一个常见缘由。每当须要增长一个新 的 if 语句时,都要被迫改动原函数。实际上,每当咱们看到一大片的 if 或者 swtich-case 语句时,第一时间就应该考虑,可否利用对象的多态性来重构它们。编辑器

利用多态的思想

利用对象的多态性来让程序遵照开放封闭原则,是一个经常使用的技巧。函数

  • 不符合OCP
var makeSound = function( animal ){
    if ( animal instanceof Duck ){ 
        console.log( '嘎嘎嘎' ); 
    } else if ( animal instanceof Chicken ) {
        console.log( '咯咯咯' );
    }
};
var Duck = function(){}; 
var Chicken = function(){};
makeSound( new Duck() ); 
makeSound( new Chicken() );

//动物世界里增长一只狗以后,makeSound 函数必须改为:
var makeSound = function( animal ){ 
    if ( animal instanceof Duck ){
        console.log( '嘎嘎嘎' ); 
    } else if ( animal instanceof Chicken ) {
        console.log( '咯咯咯' ); 
    } else if ( animal instanceof Dog ) {
        console.log('汪汪汪' ); 
    }
};
var Dog = function(){};
// 增长跟狗叫声相关的代码
 makeSound( new Dog() ); // 增长一只狗
复制代码

利用多态的思想,咱们把程序中不变的部分隔离出来(动物都会叫),而后把可变的部分封 装起来(不一样类型的动物发出不一样的叫声),这样一来程序就具备了可扩展性。当咱们想让一只狗发出叫声时,只需增长一段代码便可,而不用去改动原有的 makeSound函数post

var makeSound = function( animal ){ 
    animal.sound();
};
var Duck = function(){};
Duck.prototype.sound = function(){ 
    console.log( '嘎嘎嘎' );
};
var Chicken = function(){};
Chicken.prototype.sound = function(){ 
    console.log( '咯咯咯' );
};
makeSound( new Duck() ); // 嘎嘎嘎
makeSound( new Chicken() ); // 咯咯咯
/********* 增长动物狗,不用改动原有的 makeSound 函数 ****************/
var Dog = function(){}; Dog.prototype.sound = function(){
console.log( '汪汪汪' ); };
makeSound( new Dog() ); // 汪汪汪
复制代码
放置挂钩

放置挂钩(hook)也是分离变化的一种方式。咱们在程序有可能发生变化的地方放置一个挂钩,挂钩的返回结果决定了程序的下一步走向。这样一来,本来的代码执行路径上就出现了一个 分叉路口,程序将来的执行方向被预埋下多种可能性。ui

使用回调函数

在 JavaScript 中,函数能够做为参数传递给另一个函数,这是高阶函数的意义之一。在这 种状况下,咱们一般会把这个函数称为回调函数。在 JavaScript版本的设计模式中,策略模式和命令模式等均可以用回调函数轻松实现。 回调函数是一种特殊的挂钩。咱们能够把一部分易于变化的逻辑封装在回调函数里,而后把 回调函数看成参数传入一个稳定和封闭的函数中。当回调函数被执行的时候,程序就能够由于回 调函数的内部逻辑不一样,而产生不一样的结果。this

总结

开放封闭原则是一个看起来比较虚幻的原则,但咱们仍是能找到一些让程序尽可能遵照开放封闭原则的规律,最明显的就是找出程序中将要发生变化的地方,而后把变化封装起来。 经过封装变化的方式,能够把系统中稳定不变的部分和容易变化的部分隔离开来。在系统的 演变过程当中,咱们只须要替换那些容易变化的部分,若是这些部分是已经被封装好的,那么替换起来也相对容易。而变化部分以外的就是稳定的部分。在系统的演变过程当中,稳定的部分是不须要改变的。spa

系列文章:

《JavaScript设计模式与开发实践》最全知识点汇总大全prototype