设计原则和编程技巧 可以说每种设计模式都是为了让代码迎合其中一个或多个原则而出现的,它们本身已经融入了设计模式之中,给面向对象编程指明了方向。 前辈总结的设计原则通常值的是单一职责原则、里氏替换原则、依赖倒置原则、接口隔离原则、合成复用原则和最少知识原则。

单一职责原则(SRP)

SRP原则体现在:一个对象(方法)只做一件事情。如果一个方法承担了太多的职责,并且这些职责耦合在一起,一个职责发生变化影响到另外的职责的实现。这种耦合性得到的是低内聚和脆弱的设计。 如果两个职责总是同时变化的,那么也不必一定要分离他们。有时即使耦合在一起,但没有发生改变的征兆,那么也许也可以不必主动分离他们,而是等到需要重构的时候。 有时候为了方便性,我们可以选择违反规则,去牺牲稳定性。比如 jQuery 的 attr 方法,内部实现很复杂庞大,维护会有一些困难,但对于使用者很方便。

优缺点

优点是,降低了单个类或者对象的复杂度,有助于代码复用和进行单元测试。当一个职责需要改变的时候,不会影响到另外一个职责。缺点是,增加代码复杂度,增加了对象之间相互联系的复杂度。

最少知识原则(LKP)

LKP 说的是一个软件实体应当尽可能少地与其它实体发生相互作用。设计模式中的体现:

  • 中介者模式(增加一个中介者对象,让所有相关的对象都通过中介者对象来通信)
  • 外观模式(隔离客户与复杂子系统的之间的联系,客户不用去了解子系统的细节)

原则只是一种指导,要根据具体的环境来决定是否遵守。

开放-封闭原则(OCP)

软件实体(类、模块、函数)等应该是可以扩展的,但是不可修改。

  • 开放和封闭:比起修改源代码,增加代码是成本更低,且更不容易出 bug 的方式。
  • 利用对象多态性消除条件分支。
  • 找出变化的地方,封装变化,可以把系统中稳定不变的部分和容易变化的部分隔离开来。

最初编写代码的时候,因为需求排期不是无限的,可以先假设变化永远不会发生,这样有利于迅速完成需求。当变化发生且对我们接下来的工作造成影响了,可以回过头来封装这些变化的地方,然后确保我们不会掉进同一个坑里。

接口和面向接口编程

通常讲到的接口

  1. API 接口,主动暴露接口来通信。
  2. 一些语言提供的关键字,比如 Java 的 interface。
  3. 更加抽象,接口时对象能响应的请求的集合。

传统的面向对象语言比如 Java,会有抽象类和 interface 来限制,而 JavaScript 是一个动态类型语言,类型本身在 JavaScript 是一个相对模糊的概念。可以认为,JavaScript 中的非基础类型都可以看成“天生”被“向上转型”成了 Object。因为不需要进行向上转型,接口在 JavaScript 中的最大作用就退化到了检查代码的规范性。在 JavaScript 中,我们一般会手动编写一些接口检查的代码。

代码重构

1、提炼函数

需要重构的函数:如果一个函数过长,并且需要很多注释才能让这个函数显得易读一些。将可以独立出来的代码拿出来放进另一个独立的函数,有以下好处:

  • 避免出现超大函数。
  • 独立出来的函数有助于函数复用。
  • 独立出来的函数更容易被覆写。
  • 独立出来的函数如果有一个良好的命名,它本身就起到到了注释的作用。

2、合并重复的条件片段

3、把条件语句提炼成函数

比如: age > 50 && gender = 'woman'  改成 isOldWoman() 

4、合理使用循环

如果有些代码负责的是一些重复性的工作,那么可以使用循环完成同样的功能。

5、提前让函数退出代替嵌套条件语句

6、传递对象参数避免过长的参数列表

7、尽量减少参数数量

8、少用三目运算符

条件分支逻辑简单时,可以使用三目运算符,但是如果条件分支逻辑非常复杂,则用if、else会更合适,因为阅读更加容易,且方便修改。

9、合理使用链式调用

链式调用的时候,虽然可以节省一些中间变量。但带来的坏处时在调试的时候非常不方便,如果一条链中有错误出现,我们必须得先将这条链拆开才能加上一些 log  或者增加断点。除非链条相对稳定,且不容易发生修改,否则还是选择使用普通调用的形式。

10、分解大型类

面向对象设计鼓励将行为分布在合理数量的更小对象之中。把行为委托给这些其它类的对象来执行。

11、用 return 推出循环