文章目录
SOLID原则
S:单一职责原则 Single Responsibility Principle
修改一个类的原因只能有一个。
示例
我们有几个理由来对 员工(Employee)类 进行修改。
-
与该类的主要工作(管理员工数据)有关。
-
时间表报告的格式可能会随着时间而改变,从而使你需要对类中的代码进行修改。
解决该问题的方法是将与打印时间表报告相关的行为移动到一个单独的类中。
这个改变让你能将其他与报告相关的内容移动到一个新的类中。
O:开闭原则 Open/closed Principle
对于扩展,类应该是“开放”的;对于修改,类则应是“封闭”的。
示例
你的电子商务程序中包含一个计算运输费用的 订单(Order)类 ,该类中所有运输方法都以硬编码的方式实现。
如果你需要添加一个新的运输方式,那就必须承担对 订单类 造成破坏的可能风险来对其进行修改。
你可以通过应用策略模式来解决这个问题。
首先将运输方法抽取到拥有同样接口的不同类中。
现在, 当需要实现一个新的运输方式时, 你可以通过扩展 运输(Shipping)接口 来新建一个类, 无需修改任何订单类的代码。
当用户在 UI 中选择这种运输方式时,订单类 客户端代码会将订单链接到新类的运输方式对象。
此外,根据单一职责原则,这个解决方案能够让你将运输时间的计算代码移动到与其相关度更高的类中。
L:里氏替换原则 Liskov Substitution Principle
当你扩展一个类时, 记住你应该要能在不修改客户端代码的情况下将子类的对象作为父类对象进行传递。
示例
让我们来看看一个违反替换原则的文档类层次结构例子。
只读文件(ReadOnlyDocuments)子类 中的 保存(save)方法 会在被调用时抛出一个异常。
基础方法则没有这个限制。这意味着如果我们没有在保存前检查文档类型,客户端代码将会出错。
代码也将违反开闭原则,因为客户端代码将依赖于具体的文档类。
如果你引入了新的文档子类,则需要修改客户端代码才能对其进行支持。
你可以通过重新设计类层次结构来解决这个问题:一个子类必须扩展其超类的行为,因此只读文档变成了层次结构中的基类。
可写文件现在变成了子类,对基类进行扩展并添加了保存行为。
I:接口隔离原则 Interface Segregation Principle
客户端不应被强迫依赖于其不使用的方法。
示例
假如你创建了一个程序库,它能让程序方便地与多种云计算供应商进行整合。
尽管最初版本仅支持阿里云服务,但它也覆盖了一套完整的云服务和功能。
假设所有云服务供应商都与阿里云一样提供相同种类的功能。
但当你着手为其他供应商提供支持时,程序库中绝大部分的接口会显得过于宽泛。
其他云服务供应商没有提供部分方法所描述的功能。
尽管你仍然可以去实现这些方法并放入一些桩代码,但这绝不是优良的解决方案。
更好的方法是将接口拆分为多个部分。
能够实现原始接口的类现在只需改为实现多个精细的接口即可。
其他类则可仅实现对自己有意义的接口。
与其他原则一样,你可能会过度使用这条原则。
不要进一步划分已经非常具体的接口。
记住,创建的接口越多,代码就越复杂。
因此要保持平衡。
D:依赖倒置原则 Dependency Inversion Principle
高层次的类不应该依赖于低层次的类, 两者都应该依赖于抽象接口。
抽象接口不应依赖于具体实现,具体实现应该依赖于抽象接口。
示例
在本例中, 高层次的预算报告类(BudgetReport)使用低层次的数据库类(MySQLDatabase)来读取和保存其数据。
这意味着低层次类中的任何改变(例如当数据库服务器发布新版本时)都可能会影响到高层次的类,但高层次的类不应关注数据存储的细节。
要解决这个问题,你可以创建一个描述读写操作的高层接口,并让报告类使用该接口代替低层次的类。
然后你可以修改或扩展低层次的原始类来实现业务逻辑声明的读写接口。
其结果是原始的依赖关系被倒置:现在低层次的类依赖于高层次的抽象。