SOLID Principles - tenji/ks GitHub Wiki
SOLID:面向对象设计的前 5 个原则
在软件编程中,SOLID 是五个设计原则的助记首字母缩写词,旨在使面向对象的设计更易于理解、灵活和可维护。这些原则建立了软件开发实践,并考虑随着项目的发展维护和扩展软件。采用这些实践还可以帮助避免代码异味、重构代码以及开发敏捷或自适应软件。
SOLID 代表:
- Single-responsibility Principle
- Open-closed Principle
- Liskov Substitution Principle
- Interface Segregation Principle
- Dependency Inversion Principle
一、起源
软件工程师兼讲师 Robert C. Martin 在他 2000 年关于软件 rot 的论文《设计原则和设计模式》中介绍了一系列原则。SOLID 缩写是 Michael Feathers 在 2004 年左右创造的。
二、Single-responsibility Principle
单一职责原则:一个类发生变化的原因永远不应该超过一个。换句话说,每个类应该只有一个责任。
2.1 Java 样例
Bad Example
public class Invoice {
public void AddInvoice() {
// your logic
}
public void DeleteInvoice() {
// your logic
}
public void GenerateReport() {
// your logic
}
public void EmailReport() {
// your logic
}
}
如果你看一下 Invoice 类,它现在正在处理多重职责,这不满足单一职责原则。因此,为了满足 Invoice 类的单一责任原则,我们将方法分为不同的类,其中一个类只负责一种责任。我们将把 AddInvoice() 和 DeleteInvoice() 方法合并到单个 Invoice 类中,因为它们执行类似的功能。而我们将分别为方法 GenerateReport() 和 EmailReport() 创建单独的类 Report 和 Email,因为它们是完全独立的并且具有不同的功能。
Good Example
public class Invoice {
public void AddInvoice() {
// your logic
}
public void DeleteInvoice() {
// your logic
}
}
public class Report {
public void GenerateReport() {
// your logic
}
}
public class Email {
public void EmailReport() {
// your logic
}
}
三、Open-closed Principle (OCP)
开闭原则 (OCP) :对象或实体应该对扩展开放,但对修改关闭。
3.1 Java 样例
可以参考简单工厂模式的代码示例,就是违反 OCP 原则的,因为每次添加新产品都需要修改工厂类,传送门
3.2 优点
- 提高软件的可维护性和可扩展性;
- 降低开发成本和维护成本;
- 减少引入 BUG 的风险,因为避免了修改现有代码。
3.3 缺点
- 实现开闭原则可能会增加代码的复杂度;
- 在某些情况下,实现开闭原则可能比较困难。
四、Liskov Substitution Principle (LSP)
里氏替换原则:它基于“可替代性”的概念——面向对象编程中的一个原则,指出一个对象(例如类)可以被子对象(例如扩展第一个类的类)替换,而不会破坏程序。
4.1 Java 样例
Bad Example
public class Bird {
public void fly() {}
}
public class Duck extends Bird {}
public class Ostrich extends Bird{}
Ostrich 是鸟,但它不会飞,Ostrich 类是 Bird 类的子类型,但它不应该能够使用 fly 方法,这意味着我们打破了 LSP 原则。
Good Example
public class Bird {}
public class FlyingBird extends Bird {
public void fly() {}
}
public class Duck extends FlyingBird {}
public class Ostrich extends Bird {}
五、Interface Segregation Principle (ISP)
...
六、Dependency Inversion Principle (DIP)
依赖倒置原则:实体必须依赖于抽象,而不是具体。它指出高级模块不能依赖于低级模块,但它们应该依赖于抽象。
6.1 Java 样例
Bad Example
public class MySQLConnection
{
public String connect()
{
// handle the database connection
return "Database connection";
}
}
public class PasswordReminder
{
private MySQLConnection dbConnection;
public PasswordReminder(MySQLConnection dbConnection)
{
this.dbConnection = dbConnection;
}
}
首先,MySQLConnection 是低级模块,而 PasswordReminder 是高级模块,上面的代码片段违反了 DIP 原则,因为 PasswordReminder 类被迫依赖于 MySQLConnection 类。稍后,如果您要更改数据库引擎,则还必须编辑 PasswordReminder 类,这将违反开闭原则。
Good Example
interface DBConnectionInterface
{
public String connect();
}
class MySQLConnection implements DBConnectionInterface
{
public String connect()
{
// handle the database connection
return "Database connection";
}
}
class PasswordReminder
{
private DBConnectionInterface dbConnection;
public PasswordReminder(DBConnectionInterface dbConnection)
{
this.dbConnection = dbConnection;
}
}
6.2 DIP 就是多态吗?
多态性确实在该原理中发挥了作用,但是多态不等于 DIP,而是多态是用来实现倒置的。
6.3 DIP vs IoC vs DI
IoC (Inversion of Control) 是一个设计原则,它提倡我们反转面向对象设计中的各种控制,以达到各个类之间的解耦。这里“控制”的含义是除了一个类本职之外的其它所有工作,如整个软件流程的控制,依赖对象的创建或绑定等。
DI (Dependency Injection) 依赖注入是一种编程技术,其中对象或函数接收它所需的其他对象或函数,而不是在内部创建它们。
DI is about wiring, IoC is about direction, and DIP is about shape.
- DIP 是一种设计原则,它认为上层代码不应该依赖下层的实现,而应该提供接口让下层实现
- IoC 是一种设计原则,它认为代码本职之外的工作(比如依赖对象的创建或绑定)都应该由某个第三方(框架)完成
- DI 是一种编程技术,将依赖通过“注入”的方式提供给需要的类,是 DIP 和 IoC 的具体实现
∞、参考链接
- SOLID Wikipedia
- Inversion of control Wikipedia
- Dependency injection Wikipedia
- DIP vs IoC vs DI
- Single Responsibility in SOLID Design Principle