Java 中的 SOLID 原则是什么?

32 阅读4分钟

SOLID 原则概念

SOLID 原则是面向对象编程中使用的五项设计原则。遵循这些原则将帮助您开发出强大的软件。它们将使您的代码更高效、更易读、更易于维护。理解和应用这些原则可以有效地提高代码质量,并降低软件开发成本。

SOLID 是以下单词的首字母缩写:

  • 单一职责原则
  • 开放/封闭原则
  • 里氏替换原则
  • 接口隔离原则
  • 依赖倒置原则

单一职责原则

单一责任原则规定每个类必须具有单一的、集中的责任,以及单一的改变原因。

public class Employee{
  public String getDesignation(int employeeID){ // }
  public void updateSalary(int employeeID){ // }
  public void sendMail(){ // }
}

在上面的例子中,Employee该类具有一些员工类特定的行为,如getDesignationupdateSalary

此外,它还有另一个sendMail偏离Employee类的职责的方法。

此行为并非特定于此类,并且违反了单一责任原则。为了克服这个问题,您可以将该sendMail方法移至单独的类。

就是这样:

public class Employee{
  public String getDesignation(int employeeID){ // }
  public void updateSalary(int employeeID){ // }
}

public class NotificationService {
    public void sendMail() { // }
}

开放/封闭原则

根据开放/封闭原则,组件必须对扩展开放,但对修改封闭。为了理解这一原则,让我们以计算形状面积的类为例。

public class AreaCalculator(){
  public double area(Shape shape){
    double areaOfShape;
    if(shape instanceof Square){
        // calculate the area of Square
    } else if(shape instanceof Circle){
        // calculate the area of Circle
    }
    return areaOfShape;
  }

上述示例的问题是,如果Shape将来有一个新的类型实例需要计算其面积,则必须通过添加另一个条件else-if块来修改上述类。最终,您将对该Shape类型的每个新对象执行此操作。

为了解决这个问题,您可以创建一个接口并让每个类都Shape实现该接口。然后,每个类都可以提供自己的面积计算实现。这将使您的程序在将来易于扩展。

interface IAreaCalculator(){
  double area();
}

class Square implements IAreaCalculator{
  @Override
  public double area(){
    System.out.println("Calculating area for Square");
    return 0.0;
   }
}

class Circle implements IAreaCalculator{
  @Override
  public double area(){
    System.out.println("Calculating area for Circle");
    return 0.0;
   }
}

里氏替换原则

里氏替换原则指出,您必须能够用子类对象替换超类对象,而不会影响程序的正确性。

abstract class Bird{
   abstract void fly();
}

class Eagle extends Bird {
   @Override
   public void fly() { // some implementation }
}

class Ostrich extends Bird {
   @Override
   public void fly() { // dummy implementation }
}

在上面的例子中,Eagle类和Ostrich类都扩展了Bird类并重写了fly()方法。但是,Ostrich类被迫提供虚拟实现,因为它不能飞,因此如果我们用Bird它替换类对象,它的行为方式将不一样。

这违反了里氏替换原则。为了解决这个问题,我们可以为会飞的鸟创建一个单独的类并对其进行Eagle扩展,而其他鸟可以扩展一个不同的类,该类不包含任何fly行为。

abstract class FlyingBird{
   abstract void fly();
}

abstract class NonFlyingBird{
   abstract void doSomething();
}

class Eagle extends FlyingBird {
   @Override
   public void fly() { // some implementation }
}

class Ostrich extends NonFlyingBird {
   @Override
   public void doSomething() { // some implementation }
}

接口隔离原则

根据接口隔离原则,您应该构建小型、集中的接口,不要强迫客户端实现他们不需要的行为。

一个简单的例子是有一个可以计算形状的面积和体积的界面。

interface IShapeAreaCalculator(){
  double calculateArea();
  double calculateVolume();
}

class Square implements IShapeAreaCalculator{
  double calculateArea(){ // calculate the area }
  double calculateVolume(){ // dummy implementation }
}

问题在于,如果一个Square形状实现了这一点,那么它就会被迫实现calculateVolume()它不需要的方法。

另一方面,aCube可以同时实现两者。为了解决这个问题,我们可以分离接口并拥有两个单独的接口:一个用于计算面积,另一个用于计算体积。这将允许各个形状决定要实现什么。

interface IAreaCalculator {
    double calculateArea();
}

interface IVolumeCalculator {
    double calculateVolume();
}

class Square implements IAreaCalculator {
    @Override
    public double calculateArea() { // calculate the area }
}

class Cube implements IAreaCalculator, IVolumeCalculator {
    @Override
    public double calculateArea() { // calculate the area }

    @Override
    public double calculateVolume() {// calculate the volume }
}

依赖倒置原则

在依赖倒置原则中,高级模块不应该依赖于低级模块。换句话说,你必须遵循抽象并确保松散耦合

public interface Notification {
    void notify();
}

public class EmailNotification implements Notification {
    public void notify() {
        System.out.println("Sending notification via email");
    }
}

public class Employee {
    private EmailNotification emailNotification; 
    public Employee(EmailNotification emailNotification) {
        this.emailNotification = emailNotification;
    }
    public void notifyUser() {
        emailNotification.notify();
    }
}

在给定的示例中,Employee类直接依赖于EmailNotification类,而类是低级模块。这违反了依赖倒置原则。

public interface Notification{
  public void notify();
}

public class Employee{
  private Notification notification;
  public Employee(Notification notification){
      this.notification = notification;
  }
  public void notifyUser(){
    notification.notify();
  }
 }
 
 public class EmailNotification implements Notification{
    public void notify(){
        //implement notification via email 
    }
 }
 
 public static void main(String [] args){
    Notification notification = new EmailNotification();
    Employee employee = new Employee(notification);
    employee.notifyUser();
 }

在上面的例子中,我们确保了松散耦合。Employee不依赖于任何具体的实现,而是仅依赖于抽象(通知接口)。

如果我们需要改变通知模式,我们可以创建一个新的实现并将其传递给Employee