单一职责原则
单一职责原则(SRP)的职责被定义为“引起变化的原因”。如果我们有两个动机去改写一个方法,那么这个方法就具有两个职责。每个职责都是变化的一个轴线,如果一个方法承担了过多的职责,那么在需求的变迁过程中,需要改写这个方法的可能性就越大。因此,SRP原则体现为:一个对象(方法)只做一件事情
很多设计模式都遵循了SRP,比如装饰者模式
Function.prototype.after = function(afterfn) {
var __self = this;
return function() {
var ret = __self.apply(this, arguments);
afterfn.apply(this, arguments);
return ret;
}
};
// 职责一
var showLogin = function() {
console.log('打开登录浮层');
};
// 职责二
var log = function() {
console.log('上报标签为: ' + this.getAttribute('tag'));
};
// 打开登录浮层之后上报数据
document.getElementById('button').onclick = showLogin.after(log);
SRP 原则的优点是降低了单个类或者对象的复杂度,按照职责把对象分解成更小的粒度,这有助于代码的复用,也有利于进行单元测试。当一个职责需要变更的时候,不会影响到其他的职责。
SRP 原则也有一些缺点,最明显的是会增加编写代码的复杂度。当我们按照职责把对象分解成更小的粒度之后,实际上也增大了这些对象之间相互联系的难度
在实际开发中,是否选择让代码符合单一职责原则,要根据具体的环境来定。
最少知识原则
最少知识原则(LKP)说的是一个软件实体应当尽可能少地与其他实体发生相互作用。这里的软件实体是一个广义的概念,不仅包括对象,还包括系统、类、模块、函数、变量等
单一职责原则指导我们把对象划分成较小的粒度,这可以提高对象的可复用性。但越来越多的对象之间可能会产生错综复杂的联系,如果修改了其中一个对象,很可能会影响到跟它相互引用的其他对象。对象和对象耦合在一起,有可能会降低它们的可复用性。在程序中,对象的“朋友”太多并不是一件好事,“城门失火,殃及池鱼”和“一人犯法,株连九族”的故事时有发生
中介者模式是最少知识原则的很好实践,通过引入一个第三者对象,管理其他子对象,这样就减少了子对象之间的联系,维护时只需要维护第三者对象
关于中介者模式移步于此
在实际开发中,是否选择让代码符合最少知识原则,要根据具体的环境来定。
开放-封闭原则
在面向对象的程序设计中,开放封闭原则(OCP)是最重要的一条原则。很多时候,一个程序具有良好的设计,往往说明它是符合开放-封闭原则的。
开放-封闭原则: 软件实体(类、模块、函数)等应该是可以扩展的,但是不可修改
假设我们是一个大型 Web项目的维护人员,在接手这个项目时,发现它已经拥有 10万行以上的 JavaScript代码和数百个 JS文件。不久后接到一个新需求,即在 window.onload 函数中打印出页面中的所有节点数量,最简单的做法就是打开文本编辑器,搜索出 window.onload 函数在文件中的位置,在函数内部添加以下代码:
window.onload = function(){
// 原有代码略
console.log( document.getElementsByTagName( '*' ).length );
};
在项目需求变迁的过程中,想要扩展一个模块,最常用的方式当然是修改它的源代码。如果一个模块不允许修改,那么它的行为常常是固定的。然而,改动代码是一种危险的行为,很可能需求解决了,却引来了无数新的bug。按照开放-封闭原则的思想,我们希望在不改动原有代码的情况下,实现新的需求,下面使用装饰者模式打印节点数量
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函数内部实现,无论它的实现优雅或是丑陋,新增的代码和原有的代码可以井水不犯河水
让程序一开始就尽量遵守开放-封闭原则,并不是一件很容易的事情。一方面,需要知道程序在哪些地方会发生变化,这要求我们有一些“未卜先知”的能力。另一方面,留给我们的开发时间时有限的。因此,在最初编写代码的时候,先假设变化永远不会发生,这有利于我们迅速完成需求。当变化发生并且对我们接下来的工作造成影响的时候,可以再回过头来封装这些变化的地方。确保我们不会掉进同一个坑里,这有点像星矢说的:“圣斗士不会被同样的招数击倒第二次。”