作者:延晨、杨珺、营帅、雪冉、旭光我们收到的简历中几乎所有人都声称“熟悉面向对象”,但很少人能够概括面向对象方法和原则,面向对象设计和过程式设计的关键区别在哪,以及怎么定义和衡量可读性、究竟什么是可复用性、怎样的代码称为脆弱和僵化的。本文从 SOLID 原则出发,通过更贴近业务的解释性文字和具体的代码示例来回答这些问题。
什么是 SOLID?
在程序设计领域,SOLID 是由 Robert C. Martin(敏捷宣言作者之一,曾任敏捷联盟主席)在21世纪早期提出的便于记忆的首字母缩写,指代面向对象编程和面向对象设计的五个基本原则:单一功能、开闭原则、里氏替换、接口隔离以及依赖反转。在这一章我们只关注这些原则各自的定位和它们之间的关系。见下图:
概括地讲(下文会具体展开):单一职责是所有设计原则的基础,开闭原则是设计的终极目标。里氏替换原则强调的是子类替换父类后程序运行时的正确性,它用来帮助实现开闭原则。而接口隔离原则用来帮助实现里氏替换原则,同时它也体现了单一职责。依赖倒置原则是过程式设计与面向对象设计的分水岭,同时它也被用来指导接口隔离原则。
什么时候应该应用原则?
设计原则和设计模式都是经验的总结,方便分析和解决问题,但并不能用来判断是否存在问题。例如下面这段代码:
它违反了单一职责:这个 main 方法即获取了当前日期,又做了日期的处理,数据的写入。这个违反让它更难理解和维护了吗?它违反了开闭原则:很显然这个逻辑并不是可扩展的,只能通过修改的方式去增加功能。这个违反让它更难修改了吗?它违反了依赖倒置原则:main函数直接依赖底层模块fs的writeFile方法,调用main时必须和fs模块绑定。这个绑定让它无法在其他 fs 实现里复用,但是会有这个需求吗?
对于一个负责日常工作的、功能不太会扩展的小脚本而言,这段代码是比较完美的。当我们不认为它维护困难时,就不应该对它套用各种原则和模式来把问题复杂化(或过度设计)。当这段代码变得更复杂,我们发现它难以维护时,再去应用原则来诊断和分析问题。有这样的概念后,我们再来看具体的每项原则。
单一职责原则
内容:一个类只负责一件事,只有一个引起它变化的原因。
单一职责希望避免的问题很简单:避免修改一个地方反而引发另一个地方的问题。如果你的项目经常发生这样的问题,