首页 > Java > java教程 > 正文

坚实的原则

Linda Hamilton
发布: 2024-10-21 06:12:02
原创
974 人浏览过

SOLID Principles

SOLID 是一组基本原则,旨在增强面向对象编程 (OOP) 中代码的可管理性和可扩展性。它由五个关键原则组成:

  1. S单一责任原则 — SRP
  2. O笔闭原则 — OCP
  3. L伊斯科夫替代原理 — LSP
  4. I接口隔离原则 — ISP
  5. D 依存倒置原理 — DIP

这些原则是由 Robert C. Martin(也称为 Uncle Bob)在 2000 年代初期提出的,此后已在软件开发社区中广泛采用。通过遵循 SOLID 原则,开发人员可以创建更易于理解、修改和扩展的代码,从而形成更健壮和可维护的软件系统。

单一职责原则(SRP)

单一职责原则是 OOP 和 SOLID 中第一个也是最基本的原则。顾名思义,这一原则的意思是“一个类应该只负责一个特定的责任”。

假设我们有一个名为 Invoice 的类,其中包含 2 个方法generateInvoice() 和 saveToFiles() 。

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }

  public void saveToFiles() {
    // code to save invoice as a file.
  }
}
登录后复制
登录后复制
登录后复制
登录后复制

这不是一个好的做法,因为 Invoice 类有两个职责。更好的方法是将这些功能分离到专用的类中。

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }
}

public class FileManager {
  public void saveToFiles(Invoice invoice) {
    // code to save invoice as a file.
  }
}
登录后复制
登录后复制
登录后复制
登录后复制

在这里,我们可以看到我们有 2 个用于用例的类:

  • 生成发票
  • 保存到文件

遵循 SRP 的好处

  • 改进的代码组织:通过将关注点分成不同的类,代码库变得更有组织性并且更易于导航。
  • 更好的可维护性:当一个类具有单一职责时,更容易理解其目的并进行更改,而不会产生意想不到的副作用。
  • 提高可重用性:具有单一职责的类更有可能在应用程序的不同部分甚至其他项目中重用。
  • 更容易测试:具有单一职责的类通常更小、更集中,使它们更容易单独测试。

开闭原则(OCP)

开闭原则是SOLID的另一个核心原则。该原则由 Bertrand Meyer 于 1997 年提出。该原则背后的想法是“软件工件(类、模块和函数)应该对扩展开放,但对修改关闭。”

例如;

比方说,我们有一个名为 Shape 的类,我们可以使用这个类来计算形状的面积。

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }

  public void saveToFiles() {
    // code to save invoice as a file.
  }
}
登录后复制
登录后复制
登录后复制
登录后复制

在上面的代码中,添加新形状需要修改现有的 Shape 类,这不是一个好的做法。

下面是一个代码示例,演示了如何将开闭原则应用于此场景。

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }
}

public class FileManager {
  public void saveToFiles(Invoice invoice) {
    // code to save invoice as a file.
  }
}
登录后复制
登录后复制
登录后复制
登录后复制

通过OCP的应用,我们可以在不修改当前实现的情况下添加许多我们想要的形状。

注意:使用接口并不是实现 OCP 的唯一方法。

遵循 OCP 的好处

  • 降低错误风险:通过不修改现有代码,可以最大限度地降低引入新错误或破坏现有功能的风险。
  • 提高可维护性:遵循 OCP 的代码更容易维护和扩展,因为可以在不更改现有代码库的情况下添加新功能。
  • 增强灵活性:抽象和多态性的使用允许更灵活和适应性更强的设计,从而更容易适应不断变化的需求。

里氏替换原理 (LSP)

里氏替换原则是 OOP 中的另一个重要原则。它是由 Barbara Liskov 于 1987 年在一次关于数据抽象的会议演讲中引入的。

该原则指出,“超类的对象应该可以用其子类的对象替换,而不改变程序的正确性”。

例如,如果 Circle 和 Rectangle 是 Shape 的子类型,那么我们应该能够用 Circle 或 Rectangle 对象替换 Shape 对象,不会出现任何问题。

public class Shape {
    private String shapeType;
    private double radius;
    private double length;
    private double width;

    public Shape(String shapeType, double radius, double length, double width) {
        this.shapeType = shapeType;
        this.radius = radius;
        this.length = length;
        this.width = width;
    }

    public double area() {
        if (shapeType.equals("circle")) {
            return Math.PI * (radius * radius);
        } else if (shapeType.equals("rectangle")) {
            return length * width;
        } else {
            throw new IllegalArgumentException("Unknown shape type");
        }
    }
}

// Usage
public class Main {
    public static void main(String[] args) {
        Shape circle = new Shape("circle", 5, 0, 0);
        Shape rectangle = new Shape("rectangle", 0, 4, 6);

        System.out.println(circle.area());
        System.out.println(rectangle.area());
    }
}
登录后复制
登录后复制

如本例所示,遵循里氏替换原则意味着我们应该能够无缝地用子类实例替换超类实例。

遵循 LSP 的好处

  • 改进的代码可重用性:通过确保子类型可以替换其基本类型,使用基本类型的代码也可以与其任何子类型一起使用,从而促进代码重用。
  • 增强的可维护性:遵循LSP的代码更容易维护,因为它降低了修改或扩展代码库时引入错误的风险。
  • 更好的可测试性:LSP 使为类及其子类型编写单元测试变得更加容易,因为测试可以针对基本类型编写,并且应该适用于所有子类型。

接口隔离原则(ISP)

接口隔离原则是 Robert C. Martin 提出的五个 SOLID 原则之一。它指出:“不应强迫客户端依赖他们不使用的接口”。

换句话说,使用多个特定于任务的接口比使用一个通用接口更好。

下面的示例展示了通用接口的用法。

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }

  public void saveToFiles() {
    // code to save invoice as a file.
  }
}
登录后复制
登录后复制
登录后复制
登录后复制

使用像 MultifunctionPrinter 这样的通用接口迫使我们实现不必要的方法,这被认为是不好的做法。让我们探讨一下如何将接口隔离原则应用于此场景。

接口

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }
}

public class FileManager {
  public void saveToFiles(Invoice invoice) {
    // code to save invoice as a file.
  }
}
登录后复制
登录后复制
登录后复制
登录后复制

实施

public class Shape {
    private String shapeType;
    private double radius;
    private double length;
    private double width;

    public Shape(String shapeType, double radius, double length, double width) {
        this.shapeType = shapeType;
        this.radius = radius;
        this.length = length;
        this.width = width;
    }

    public double area() {
        if (shapeType.equals("circle")) {
            return Math.PI * (radius * radius);
        } else if (shapeType.equals("rectangle")) {
            return length * width;
        } else {
            throw new IllegalArgumentException("Unknown shape type");
        }
    }
}

// Usage
public class Main {
    public static void main(String[] args) {
        Shape circle = new Shape("circle", 5, 0, 0);
        Shape rectangle = new Shape("rectangle", 0, 4, 6);

        System.out.println(circle.area());
        System.out.println(rectangle.area());
    }
}
登录后复制
登录后复制

通过应用ISP,我们将其分成更小的、特定于角色的接口 ——例如打印机、扫描仪和传真。这允许每个类(例如 BasicPrinter、AdvancedPrinter 或 FaxMachine)仅实现相关功能,从而促进模块化并减少不必要的依赖关系。

跟随 ISP 的好处

  • 模块化和可重用代码:通过将大型接口分解为更小、更具体的接口,代码变得更加模块化和可重用。类或模块可以仅实现它们需要的接口,从而减少不必要的依赖关系,并使在系统的不同部分之间重用代码变得更容易。
  • 降低代码复杂度:当类或模块仅依赖于它们所需的接口时,代码变得不那么复杂且更容易理解。这是因为开发人员不必处理不必要的方法或依赖项。这些与其特定用例无关。
  • 提高可维护性:通过更小、更集中的界面,可以更轻松地维护代码。对一个界面的更改不太可能影响系统的其他部分,从而降低了引入错误或破坏现有功能的风险。
  • 更好的可测试性:更小、更集中的界面使得为各个组件编写单元测试变得更容易。这是因为测试可以专注于特定的行为,而不会受到不相关的方法或依赖项的影响。
  • 增加灵活性:通过遵守ISP,系统变得更加灵活并且更容易扩展或修改。可以通过创建新接口或修改现有接口来添加新功能或要求,而不会影响整个系统。

依赖倒置原则(DIP)

依赖倒置原则是SOLID的最终原则。这也是由 Robert C. Martin 介绍的。这促进了松散耦合的代码。

DIP说明了几点:

  • 高层模块不应该依赖于低层模块。
  • 两者都应该依赖于抽象。
  • 抽象不应该依赖于细节。
  • 细节应该取决于抽象。

简单来说,一个类不应该直接依赖于其他特定类(具体实现),而应该依赖于接口或抽象类。这使得代码更加灵活且更易于维护,因为您可以在不更改依赖类的情况下更换实现。

紧耦合代码(无 DIP)

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }

  public void saveToFiles() {
    // code to save invoice as a file.
  }
}
登录后复制
登录后复制
登录后复制
登录后复制

如上例所示,Computer 类直接依赖于 Keyboard 类。

松耦合代码(带 DIP)

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }
}

public class FileManager {
  public void saveToFiles(Invoice invoice) {
    // code to save invoice as a file.
  }
}
登录后复制
登录后复制
登录后复制
登录后复制

现在,Computer 依赖于 InputDevice 接口,而不是特定的 Keyboard。这样可以轻松切换到另一个输入设备,例如 WirelessKeyboard,而无需修改 Computer 类。

遵循 DIP 的好处

  • 松散耦合:通过依赖抽象而不是具体实现,代码的耦合变得不那么紧密,使得更容易更改系统的一个部分而不影响其他部分。
  • 提高可维护性:低层模块的变化不会影响高层模块,使系统更容易维护和扩展。
  • 增强的可测试性:可以使用低级模块的模拟实现来测试高级模块,使测试更快、更可靠。
  • 提高可重用性:高层模块可以在不同的上下文中重用,而不需要更改它们所依赖的低层模块。

结论

总而言之,SOLID 原则:单一职责、开闭、里氏替换、接口隔离和依赖倒置为在面向对象编程中编写干净、可维护和可扩展的代码提供了基本准则。

通过遵守这些原则,开发人员可以创建更易于理解、修改和扩展的系统,最终带来更高质量的软件和更高效的开发流程。

概括

感谢您阅读这篇文章!我希望您现在对 SOLID 原则以及如何应用它们来增强您的项目有了深入的了解。

关注我:
  • LinkedIn — @nsadisha
  • GitHub — @nsadisha
  • 中 — @nsadisha
  • Dev.to — @nsadisha

—萨迪莎·尼萨拉

以上是坚实的原则的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板