SOLID 是一组基本原则,旨在增强面向对象编程 (OOP) 中代码的可管理性和可扩展性。它由五个关键原则组成:
这些原则是由 Robert C. Martin(也称为 Uncle Bob)在 2000 年代初期提出的,此后已在软件开发社区中广泛采用。通过遵循 SOLID 原则,开发人员可以创建更易于理解、修改和扩展的代码,从而形成更健壮和可维护的软件系统。
单一职责原则是 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 个用于用例的类:
开闭原则是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 的唯一方法。
里氏替换原则是 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()); } }
如本例所示,遵循里氏替换原则意味着我们应该能够无缝地用子类实例替换超类实例。
接口隔离原则是 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)仅实现相关功能,从而促进模块化并减少不必要的依赖关系。
依赖倒置原则是SOLID的最终原则。这也是由 Robert C. Martin 介绍的。这促进了松散耦合的代码。
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 类。
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 类。
总而言之,SOLID 原则:单一职责、开闭、里氏替换、接口隔离和依赖倒置为在面向对象编程中编写干净、可维护和可扩展的代码提供了基本准则。
通过遵守这些原则,开发人员可以创建更易于理解、修改和扩展的系统,最终带来更高质量的软件和更高效的开发流程。
感谢您阅读这篇文章!我希望您现在对 SOLID 原则以及如何应用它们来增强您的项目有了深入的了解。
关注我:
—萨迪莎·尼萨拉
以上是坚实的原则的详细内容。更多信息请关注PHP中文网其他相关文章!