前言
同学们,我们学习设计模式已经很长的时间了,今天将会是我们设计模式系列的最后一章节,编程设计原则和设计规则。设计模式对于一个开发工程师而言是一个很重要但是又不容易展现的一个能力,但是也是能否成为架构师的关键,希望大家可以沉下心学习并且理解,因为篇幅的限制,我们每一系列的例子并不是那么多,需要大家沉下心在工作学习中进一步体会
正文
单一职责原则
单一职责原则(SRP)的职责被定义为“引起变化的原因”。如果我们有两个动机去改写一个方法,那么这个方法就具有两个职责。每个职责都是变化的一个轴线,如果一个方法承担了过的职责,那么在需求的变迁过程中,需要改写这个方法的可能性就越大。
此时,这个方法通常是一个不稳定的方法,修改代码总是一件危险的事情,特别是当两个职责耦合在一起的时候,一个职责发生变化可能会影响到其他职责的实现,造成意想不到的破坏,这种耦合性得到的是低内聚和脆弱的设计。
前面的章节已经总结了很多类似的例子,归根究底,一个函数只负责一个职责,不要将多个职责赋予给一个函数,这样可能会导致这个单一函数的频繁迭代,不记得的同学可以看一下我们之前代理模式那一章数据埋点的例子
最少知识原则
最少知识原则(LKP)说的是一个软件实体应当尽可能少地与其他实体发生相互作用。这里的软件实体是一个广义的概念,不仅包括对象,还包括系统、类、模块、函数、变量等。
单一职责原则指导我们把对象划分成较小的粒度,这可以提高对象的可复用性。但越来越多的对象之间可能会产生错综复杂的联系,如果修改了其中一个对象,很可能会影响到跟它相互引用的其他对象。
最少知识原则要求我们在设计程序时,应当尽量减少对象之间的交互。如果两个对象之间不必彼此直接通信,那么这两个对象就不要发生直接的相互联系。常见的做法是引入一个第三者对象,来承担这些对象之间的通信作用。如果一些对象需要向另一些对象发起请求,可以通过第三者对象来转发这些请求。
最常见的例子就是我们之前介绍的中介者模式,这样可以避免对象粒度过小导致对象过多之间交互缠绕的问题
开放-封闭原则
软件实体(类、模块、函数)等应该是可以扩展的,但是不可修改
在面向对象的程序设计中,开放-封闭原则(OCP)是最重要的一条原则。很多时候,一个程序具有良好的设计,往往说明它是符合开放-封闭原则的。
关于这个例子我们之前几乎每一章都有提到,程序设计的过程中,我们应该要允许功能的迭代,但是同时我们也要尽可能避免对工具类函数本身的修改,对于这个问题,我们可以抽离出容易改变的部分,以函数参数的形式传递给工具类函数本身来实现迭代,这样就避免了对工具类函数本身的修改
为什么要遵循这样一个原则,因为在项目开发的过程中,并不是一个人在操作,我们应该尽量保证一个原则,在迭代的过程中,尽量不要修改到不是我们自己写的代码,扩展但不修改原工具函数,来避免多人合作出错的可能性
代码重构
因为篇幅的限制可能没办法对每种情况进行详细地代码说明,这里只能较简略地说明大致代码重构的思路
提炼函数
在 JavaScript 开发中,我们大部分时间都在与函数打交道,所以我们希望这些函数有着良好的命名,函数体内包含的逻辑清晰明了。如果一个函数过长,不得不加上若干注释才能让这个函数显得易读一些,那这些函数就很有必要进行重构。
如果在函数中有一段代码可以被独立出来,那我们最好把这些代码放进另外一个独立的函数中。这是一种很常见的优化工作,这样做的好处主要有以下几点:
- 避免出现超大函数
- 独立出来的函数有助于代码复用
- 独立出来的函数更容易被覆写
- 独立出来的函数如果拥有一个良好的命名,它本身就起到了注释的作用
合并重复的条件片段
如果一个函数体内有一些条件分支语句,而这些条件分支语句内部散布了一些重复的代码,那么就有必要进行合并去重工作。
比如像上面的例子,console.log(1)是一个公共的部分,所以,我们其实可以把它从条件分支语句中提取出来
把条件分支语句提炼成函数
还记得我们对策略模式的介绍吗,对于大量的if语句,在程序设计中是不受欢迎的写法,我们可以把这些if语句封装成各自的策略,用策略的切换来实现替换if语句,一方面,这样避免了出现大量if语句,第二方面,当有一种新的策略的时候,我们不需要在工具类里叠加新的if语句,而是只需要定义一种新的策略,以高阶函数的形式传进去就可以
合理使用循环
在函数体内,如果有些代码实际上负责的是一些重复性的工作,那么合理利用循环不仅可以完成同样的功能,还可以使代码量更少。
提前让函数退出代替嵌套条件分支
嵌套的条件分支语句绝对是代码维护者的噩梦,对于阅读代码的人来说,嵌套的 if、else语句相比平铺的 if、else,在阅读和理解上更加困难,有时候一个外层 if 分支的左括号和右括号之间相隔 500 米之远。用《重构》里的话说,嵌套的条件分支往往是由一些深信“每个函数只能有一个出口的”程序员写出的。但实际上,如果对函数的剩余部分不感兴趣,那就应该立即退出。引导阅读者去看一些没有用的 else 片段,只会妨碍他们对程序的理解。
传递对象参数代替过长的参数列表
有时候一个函数有可能接收多个参数,而参数的数量越多,函数就越难理解和使用。使用该函数的人首先得搞明白全部参数的含义,在使用的时候,还要小心翼翼,以免少传了某个参数或者把两个参数搞反了位置。所以,我们可以直接传递对象参数,一方面不需要再考虑参数之间的次序问题,另一方面也不需要对象的参数名也可以方便别人更容易理解你的参数意义
尽量减少参数数量
如果调用一个函数时需要传入多个参数,那这个函数是让人望而生畏的,我们必须搞清楚这些参数代表的含义,必须小心翼翼地把它们按照顺序传入该函数。而如果一个函数不需要传入任何参数就可以使用,这种函数是深受人们喜爱的。在实际开发中,向函数传递参数不可避免,但我们应该尽量减少函数接收的参数数量。
少用三目运算符
有一些程序员喜欢大规模地使用三目运算符,来代替传统的 if、else。理由是三目运算符性能高,代码量少。不过,这两个理由其实都很难站得住脚。,相比损失的代码可读性和可维护性,三目运算符节省的代码量也可以忽略不计。让 JS文件加载更快的办法有很多种,如压缩、缓存、使用 CDN 和分域名等。
合理使用链式调用
使用链式调用的方式并不会造成太多阅读上的困难,也确实能省下一些字符和中间变量,但节省下来的字符数量同样是微不足道的。链式调用带来的坏处就是在调试的时候非常不方便,如果我们知道一条链中有错误出现,必须得先把这条链拆开才能加上一些调试 log 或者增加断点,这样才能定位错误出现的地方。
如果该链条的结构相对稳定,后期不易发生修改,那么使用链式调用无可厚非。但如果该链条很容易发生变化,导致调试和维护困难,那么还是建议使用普通调用的形式
用 return 退出多重循环
避免使用break来退出多重循环,这会导致代码层面上的难以理解,提高理解成本,尝试使用return来退出多重循环
小结
同学们,关于设计模式的学习到这里就已经接近尾声了,希望大家可以通过学习设计模式,培养自己在写代码中的那种良好习惯,后面我可能更多地会把重心放到技术栈层面以及算法层面,希望对大家有帮助!