“这是我参与8月更文挑战的第2天,活动详情查看:8月更文挑战”前端
在面试高级前端时,每每会遇到一些关于设计模式的问题,每次都回答不太理想。恰逢8月更文挑战的活动,准备用一个月时间好好理一下关于设计模式方面的知识点,给本身增长点面试的底气。程序员
在学习设计模式以前,首先要认识到设计模式是个编程思想,对任何编程语言都适用。其次要从设计模式的原则开始学习,故本文将详细介绍设计模式的原则之一开放封闭原则。web
开放-封闭原则是说软件实体(类、模块、函数等等)应该能够扩展,可是不可修改。面试
在开发任何系统时,不要期望系统一开始时需求肯定,就不再会发生变化,这是不切实际的想法,既然需求是必定会发生变化的,那么如何设计才能面对需求的改变,不至于修改大部分的代码致使系统不稳定。ajax
既然修改会致使系统不稳定,那么就少修改,不改变代码的主题,使用扩展的方式添加新需求的代码。这就是开放-封闭,是对扩展是开放的,对修改是封闭的。编程
举一个职场中很常见的现象来解释开放-封闭原则。弹性打卡制度,你们都很熟悉吧,这个制度就是一个很好的开放-封闭原则的应用。设计模式
在一家小公司,8点上班,可是有几个骨干员工常常迟到,老板看眼里,内心想这种现象很是很差,因而叫来人事主管提起这个现象,说日后迟到要扣钱,人事主管听了跟老板建议到:“我从考勤记录得知那几个骨干晚上都加班比较晚,加上咱们又没有给加班费,这么作,不免会让人心生不满致使人员流失,建议改为弹性上班,好比早上8点到10点弹性,晚上6点到8点弹性下班”。老板听了,想到仍是一天工做8个小时没变,因而说到:“先按这样执行一段时间,看看效果”。markdown
在以上的案例中,老板说日后迟到要扣钱,就是修改本来的考勤逻辑代码,可能会致使员工(系统)离职(不稳定)。而人事主管改为弹性上班的建议,只是本来的考勤逻辑代码中扩展出一种计算考勤的方法,其考勤时间仍是8个小时不变的,不会致使员工(系统)离职(不稳定)。app
给Function
的原型链上添加一个extend
方法来对函数进行扩展,在extend
中利用_this.apply(this , arguments)
执行要扩展的函数,并将直接结果result
返回。而后执行fun.apply(this , arguments)
,其中fun
就是调用函数extend
时传入的要扩展的函数。这样不去修改函数的原有代码,也能往函数中添加新的逻辑,实现了开放-封闭。编程语言
Function.prototype.extend = function(fun){
var _this = this;
return function(){
const result = _this.apply(this , arguments);
fun.apply(this , arguments);
return result
}
}
const a = () =>{
//旧的逻辑代码
}
const b = a.extend(() =>{
//新增逻辑代码
})
复制代码
多态的含义:同一操做做用于不一样的对象上面,能够产生不一样的解释和不一样的执行结果。或者换句话说,给不一样的对象发送同一个消息的时候,这些对象会根据这个消息分别给出不一样的 反馈。
多态的思想是将“作什么”和“谁去作以及怎样去作”分离开来,也就是将“不变的事物”与“可能改变的事物”分离开来。正好符合开放-封闭原则。
下面先提供一段不符合开放-封闭原则的代码,再利用多态的思想来将其改形成符合开放-封闭原则。
class A {
constructor() {}
init() {
console.log('初始化A');
}
}
class B {
constructor() {}
init() {
console.log('初始化B');
}
}
const init = (type) => {
if (type === 'A') {
new A().init();
} else if (type === 'B') {
new B().init();
}
};
init('A');
init('B');
复制代码
在init
函数中经过判断传入type
来分别初始化A类和B类,倘若又来一个C类,要用init
函数来初始化,要怎么办呢?直接修改init
函数:
class C {
constructor() {}
init() {
console.log('初始化C');
}
}
const init = (type) => {
if (type === 'A') {
new A().init();
} else if (type === 'B') {
new B().init();
}else if(type === 'C'){
new C().init();
}
};
复制代码
如果这样处理,日后每新增一个类要初始化,都要去改动init
函数的内部实现,是违背了开放-封闭原则。
利用多态的思想,把程序中不变的部分隔离出来(都会调用类的init
方法进行初始化), 而后把可变的部分(类的实例化)封装起来,这样一来程序就具备了可扩展性。故能够这样改造init
函数。
const init = (obj) =>{
if(obj.init instanceof Function){
obj.init();
}
}
init(new A());
init(new B());
init(new C());
复制代码
函数能够做为参数传递给另一个函数,把这个函数称为回调函数。
能够把一部分易于变化的逻辑封装在回调函数中,而后把回调函数看成参数传入一个稳定和封闭的函数中,该函数是封闭的,不能轻易修改的。
当一个函数要扩展时,能够把扩展的逻辑写入回调函数,当回调函数被执行时,就至关函数被扩展了,从而实现了开放-封闭原则。
Jq的ajax
就是利用了回调函数进行扩展,每次请求回来的数据都用回调函数进行处理。
var getUserInfo = function( callback ){
$.ajax( 'http:// xxx.com/getUserInfo', callback );
};
getUserInfo( function( data ){
console.log( data.userName );
});
getUserInfo( function( data ){
console.log( data.userId );
});
复制代码
在函数中容易发生变化的地方放置钩子函数,当函数执行到该地方时就会触发钩子函数,钩子函数中写入扩展的逻辑,就至关函数被扩展了,从而实现了开放-封闭原则。
遵循开放-封闭原则的开发过程当中,最难的是要找到将要发生变化的地方。将变化的封装起来,能够把系统中稳定不变的部分和容易变化的部分隔离开来。在系统的演变过程当中,咱们只须要替换那些容易变化的部分,若是这些部分是已经被封装好的,那么替换起来也相对容易。而变化部分以外的就是稳定的部分。在系统的演变过程当中,稳定的部分是不须要改变的。
开发时一开始就尽可能遵照开放-封闭原则,并非一件很容易的事情。
一方面,咱们须要尽快知道程序在哪些地方会发生变化,这要求咱们有一些未卜先知的能力。
另外一方面,留给程序员的需求排期并非无限的,因此咱们能够说服本身去接受不合理的代码带来的坑。
在最初开发的时候,先假设变化永远不会发生,这有利于咱们迅速完成需求。当变化发生并 且对咱们接下来的工做形成影响的时候,能够再回过头来封装这些变化的地方。而后确保咱们不 会掉进同一个坑里。