面向对象设计的 SOLID 原则

2025-06-10

面向对象设计的 SOLID 原则

什么是 SOLID?

SOLID 帮助您编写易于维护、扩展和理解的代码。

它是以下 5 项原则的首字母缩写:

S = Single-responsibility principle
O = Open-closed principle
L = Liskov substitution principle
I = Interface segregation principle
D = Dependency inversion principle
Enter fullscreen mode Exit fullscreen mode

单一职责原则

  • 一个类/模块应该只负责一件事。
  • 一个类/模块应该只有一个被改变的原因。

下面是一个违反此原则的例子:

public class Customer {
    public void add(Database db) {
        try {
            db.execute("INSERT INTO...");
        } catch (Exception e) {
            File.writeAllText("/var/log/error.log", e.toString());
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

该类Customer负责写入数据库和写入日志文件。

  • 如果我们想改变记录错误的方式,Customer就需要改变。
  • 如果我们想改变写入数据库的方式,Customer就需要改变。

该代码应重构为:

class Customer {
    private FileLogger logger = new FileLogger();
    void add(Database db) {
        try {
            db.execute("INSERT INTO...");
        } catch (Exception e) {
            logger.log(e.toString());
        }
    }
}
class FileLogger {
    void log(String error) {
        File.writeAllText("/var/log/error.log", error);
    }
}
Enter fullscreen mode Exit fullscreen mode

其他需要单独类的示例包括:用户输入验证、身份验证、缓存。

注意不要过度碎片化代码(创建过多的职责)。记住,SOLID 的重点是让你的代码更易于维护。

进一步阅读:

开放封闭原则

  • 类/模块应该对扩展开放,但对修改关闭。
  • 通过添加新代码而不是更改现有代码来扩展功能。
  • 目标是达到永远不会破坏系统核心的程度。

下面是一个违反此原则的例子:

public void pay(Request request) {
    Payment payment = new Payment();
    if (request.getType().eq("credit")) {
        payment.payWithCreditCard();
    } else {
        payment.payWithPaypal();
    }
}
public class Payment {
    public void payWithCreditCard() {
        // logic for paying with credit card
    }
    public void payWithPaypal() {
        // logic for paying with paypal
    }
}
Enter fullscreen mode Exit fullscreen mode

如果我们想添加一种新的付款方式怎么办?我们必须修改该类Payment,这违反了开放封闭原则。

该代码应重构为:

public void pay(Request request) {
    PaymentFactory paymentFactory = new PaymentFactory();
    payment = paymentFactory.initialisePayment(request.getType());
    payment.pay();
}
//-----------------------
public class PaymentFactory {
    public Payment intialisePayment(String type) {
        if (type.eq("credit")) {
            return new CreditCardPayment();
        } elseif (type.eq("paypal")) {
            return new PaypalPayment();
        } elseif (type.eq("wire")) {
            return new WirePayment();
        }
        throw new Exception("Unsupported payment method");
    }
}
//-----------------------
interface Payment {
    public void pay();
}
class CreditCardPayment implements Payment {
    public void pay() {
        // logic for paying with credit card
    }
}
class PaypalPayment implements Payment {
    public void pay() {
        // logic for paying with paypal
    }
}
class WirePayment implements Payment {
    public void pay() {
        // logic for paying with wire
    }
}
Enter fullscreen mode Exit fullscreen mode

现在我们可以通过添加新类来添加新的付款方式,而不是修改现有的类。

进一步阅读:

里氏替代原则

  • 如果一个类实现了一个接口,它必须能够替代实现该接口的任何引用。
  • MySQL例如,如果一个名为implements的类Database,而另一个名为MongoDBimplements的类Database,则您应该能够MySQL用对象替换对象MongoDB

下面是一个违反此原则的例子:

public abstract class Bird {
    public abstract void Fly();
}
public class Parrot : Bird {
    public override void Fly() {
        // logic for flying
    }
}
public class Ostrich : Bird {
    public override void Fly() {
         // Can't implement as ostriches can't fly
        throw new NotImplementedException();
    }
}
Enter fullscreen mode Exit fullscreen mode

上述设计很糟糕,因为Bird假设所有的鸟都能飞。

然后可以将此代码重构为:

public abstract class Bird {
}
public abstract class FlyingBird : Bird {
    public abstract void Fly();
}
public class Parrot : FlyingBird {
    public override void Fly() {
        // logic for flying
    }
}
public class Ostrich : Bird {
}
Enter fullscreen mode Exit fullscreen mode

该原则的要点是使用多态性和继承时要小心谨慎。

以下是这一原则的另一个更现实的遭遇:

  • BankAccount你有一个带有方法的withdrawal()。所有银行账户都允许提款吗?例如,定期存款账户就不允许提款。

进一步阅读:

接口隔离原则

  • 任何客户端都不应被迫依赖其不使用的方法。

下面是一个违反此原则的例子:

public interface Athlete {
    void compete();
    void swim();
    void highJump();
    void longJump();
}
public class JohnDoe implements Athlete {
    @Override
    public void compete() {
        System.out.println("John Doe started competing");
    }
    @Override
    public void swim() {
        System.out.println("John Doe started swimming");
    }
    @Override
    public void highJump() {
    }
    @Override
    public void longJump() {
    }
}
Enter fullscreen mode Exit fullscreen mode

JohnDoe只是一名游泳运动员,却被迫实施他永远不会使用的highJump方法。longJump

然后可以将此代码重构为:

public interface Athlete {
    void compete();
}
public interface SwimmingAthlete extends Athlete {
    void swim();
}
public interface JumpingAthlete extends Athlete {
    void highJump();
    void longJump();
}
public class JohnDoe implements SwimmingAthlete {
    @Override
    public void compete() {
        System.out.println("John Doe started competing");
    }
    @Override
    public void swim() {
        System.out.println("John Doe started swimming");
    }
}
Enter fullscreen mode Exit fullscreen mode

现在,JohnDoe不必执行他没有能力执行的操作。

进一步阅读:

依赖倒置原则

  • 高级模块不应该依赖于低级模块。它们应该依赖于抽象。
  • 这使得您可以轻松地更改实现,而无需更改高级代码。

下面是一个违反此原则的例子:

public class BackEndDeveloper {
    public void writeJava() {
    }
}
public class FrontEndDeveloper {
    public void writeJavascript() {
    }
}
public class Project {
    private BackEndDeveloper backEndDeveloper = new BackEndDeveloper();
    private FrontEndDeveloper frontEndDeveloper = new FrontEndDeveloper();
    public void implement() {
        backEndDeveloper.writeJava();
        frontEndDeveloper.writeJavascript();
    }
}
Enter fullscreen mode Exit fullscreen mode

该类Project是一个高级模块,它依赖于诸如BackEndDeveloper和 之类的低级模块FrontEndDeveloper。这违反了该原则。

该代码应重构为:

public interface Developer {
    void develop();
}
public class BackEndDeveloper implements Developer {
    @Override
    public void develop() {
        writeJava();
    }
    private void writeJava() {
    }
}
public class FrontEndDeveloper implements Developer {
    @Override
    public void develop() {
        writeJavascript();
    }
    public void writeJavascript() {
    }
}
//-----------------------
public class Project {
    private List<Developer> developers;
    public Project(List<Developer> developers) {
        this.developers = developers;
    }
    public void implement() {
        developers.forEach(d -> d.develop());
    }
}
Enter fullscreen mode Exit fullscreen mode

现在,该类Project不依赖于较低级别的模块,而是依赖于抽象。

进一步阅读:

最后说明:不要太严格遵守 SOLID 原则

  • SOLID 设计原则是原则,而不是规则。
  • 应用 SOLID 时始终要运用常识(了解你的权衡)。
  • 通常使用 SOLID,需要花费更多时间编写代码,因此您以后可以花费更少的时间阅读它。
  • 最后,请记住将 SOLID 用作工具,而不是目标。
鏂囩珷鏉ユ簮锛�https://dev.to/seymour/the-solid-principles-of-object-oriented-design-53ho
PREV
在 WSL 中安装并使用 Postgres
NEXT
8 个最佳算法交易 Python 库