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 的具体实现

DIP vs IoC vs DI

∞、参考链接