Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Javascript常用的设计原则 #19

Open
Jouryjc opened this issue Aug 19, 2018 · 0 comments
Open

Javascript常用的设计原则 #19

Jouryjc opened this issue Aug 19, 2018 · 0 comments

Comments

@Jouryjc
Copy link
Owner

Jouryjc commented Aug 19, 2018

在 程序设计领域, SOLID(单一功能、开闭原则、里氏替换、接口隔离以及依赖反转)是由罗伯特·C·马丁在21世纪早期引入的记忆术首字母缩略字,指代了面向对象编程和面向对象设计的五个基本原则。本文对Javascript中常用的三个原则进行说明。

单一职责原则

就一个类而言,应该仅有一个引起它变化的原因。在Javascript中,类的定义并不明显,单一职责原则更多被用在对象或者函数上。

单一职责原则(Single responsibility principle)被定义为“引起变化的原因”。如果我们有两个动机去改写一个方法,那么这个方法就具有两个职责。每个职责都是变化的一个曲线,如果一个方法承担了过多的职责,那么在需求的变迁过程中,需要改写这个方法的可能性就越大。

修改代码总是一件很危险的事情。因为不能确定是否会引起其他的问题。因此,SRP(Single responsibility principle的简写)体现为:一个对象(函数)只做一件事情

在设计模式中,经常会用到该原则。例如代理模式迭代器模式单例模式装饰者模式

SRP原则的应用难点就是如何去分离职责。该原则是所有原则中最简单也是最难正确运用的。在我们日常开发中,经常会用到该原则的是获取ajax请求参数和发送请求的分离。

我们也不是一定要遵守原则。例如jQuery的attr函数,就是明显违反SRP原则的做法。该方法既负责赋值,又负责取值。对于维护者来说是痛苦的,但是对于使用者却是简单方便的。

优点

  • 降低了单个类或者对象的复杂度
  • 按照职责把对象分解成小的粒度
  • 有助于代码的复用
  • 有利于单元测试

缺点

  • 增加写代码的复杂度
  • 虽然把对象分解成小的粒度,但增加了对象之间联系的难度。

最少知识原则

最少知识原则(Least Knowledge Principle)说的是一个软件实体应当尽可能少地与其他实体发生相互作用。这里的软件实体指的是系统、类、模块、函数、变量等。

比如你应该尽量少地去了解女朋友闺蜜的信息(除非单身狗),如果你和闺蜜的联系很频繁、紧密,那么一定会出问题!程序也不例外。

LKP要求我们设计程序时,应当尽量减少对象之间的联系。在之前设计模式主题中,中介者模式和外观模式都有体现LKP原则。中介者模式通过增加一个中介者对象,让所有的相关对象都通过中介者来通信,而不是彼此之间相互作用。外观模式在我们设计模式中并没有提及,是因为它在JS中用的比较少。

这里我举一个工作例子。后台有一个更新云主机信息的接口,这个接口不仅要更新基本信息(名称、描述)、也要更新配置信息(云硬盘、网络、内存、CPU)。接口不可能分成三四个API给前端调用,它需要抽象出一个接口A。A就是外观模式的表现,该模式的作用是对客户端屏蔽一组子系统的复杂性。

再举个生活中的例子。全自动洗衣机一键启动按钮也是一个外观。这个按钮屏蔽了清洗过程,比如老式洗衣机要选择好浸泡-洗衣-漂洗-脱水等步骤。不必管内部子过程的细节,就算以后更新换代,增加了消毒等其他操作。对于用户来说,他们也仅仅只需要按这一个按钮即可。

上面两个例子都说明了外观模式体现了LKP。

封装在LKP的体现。封装在很大程度上表达的是数据的隐藏。一个模块或者对象可以将内部的数据或者实现细节隐藏起来。只暴露必须的接口API供外界访问。

把变量的可见性限制在一个尽可能小的范围内,这个被改写的可能性就越小,对其他模块的影响就越小。这也是广义LKP的体现。

var mult = (function () {
    var cache = {};
    
    return function () {
        var args = Array.prototype.join.call(arguments, ',');

        if (cache[args]) {
            return cache[args];
        }

        var a = 1;
        for (var i = 0, l = arguments.length; i < l; i++) {
            a = a * arguments[i];
        }

        return cache[args] = a;
    }
})();

LKP也叫迪米特法则。迪米特法则名字虽然酷,但是很难理解。虽然是原则,但是我们也要根据具体的环境来决定要不要使用。

开放-封闭原则

开放-封闭原则(Open Closed Principle)说的是软件实体应该是可以扩展的,但是不可修改。

装饰者模式发布-订阅模式模板模式策略模式代理模式中有该原则的体现。

OCP的思想是当需要改变一个程序的功能或者给这个程序增加新功能的时候,可以使用增加代码的方式,但是不允许改动程序的源代码。

OCP的核心是找出程序中变化的地方,然后把变化封装起来。通过封装变化,可以把系统中稳定不变的部分和容易变化的部分隔离开来。在迭代演变的过程中,我们只需要替换需要改变的,保持不变的即可。

放置钩子也是分离变化的一种方式,在模板模式中我们有介绍过钩子的使用。由于子类的数量是无限的,总会有一些特殊定制的子类迫使我们不得不去改变封装好的框架。通过钩子(hook)程序就拥有了变化的可能。

回调函数也是封装变化的常用手段之一。这也是高阶函数的意义之一。数组遍历函数each、filter、map都支持传递函数来应变变化。

虽说封闭变化的手段很多,但是要做到绝对的封闭变化也是不容易的。我们只需要做到:

  • 挑选出最容易发生变化的地方,然后构造抽象来封闭这些变化。
  • 在不可避免发生修改的时候,尽量修改那些相对容易修改的地方。就像每个开源工具都提供了配置文件,它避免直接去修改源代码的麻烦。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant