在本文中,将介绍 SOLID 原则,并且将通过 Java 代码示例来帮助你理解每个原则。SOLID 原则是面向对象编程中使用的一组五个设计原则。遵循这些原则将帮助你开发健壮的软件,使你的代码更高效、可读和可维护。
SOLID 是以下原则的缩写:
单一职责原则
单一职责原则规定每个类必须有单一、明确的职责。
public class Employee{
    public String getDesignation(int employeeID){ // }
    public void updateSalary(int employeeID){ // }
    public void sendMail(){ // }
}
在上述示例中,Employee 类有一些特定于员工类的行为,如获取职位(getDesignation)和更新薪水(updateSalary)。此外,它还有一个名为 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){
            // 计算正方形的面积
        } else if(shape instanceof 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() { // 一些实现 }
}
class Ostrich extends Bird {
    @Override
    public void fly() { // 虚拟实现 }
}
在上述示例中,Eagle 类和 Ostrich 类都扩展了 Bird 类并覆盖了 fly() 方法。然而,Ostrich 类被迫提供一个虚拟实现,因为它不能飞,因此如果我们用它替换 Bird 类对象,它的行为就不一样了。
这违反了里氏替换原则。要解决此问题,我们可以为能飞的鸟创建一个单独的类,让 Eagle 扩展它,而其他鸟可以扩展一个不同的类,该类不包含任何飞行行为。
abstract class FlyingBird{
    abstract void fly();
}
abstract class NonFlyingBird{
    abstract void doSomething();
}
class Eagle extends FlyingBird {
    @Override
    public void fly() { // 一些实现 }
}
class Ostrich extends NonFlyingBird {
    @Override
    public void doSomething() { // 一些实现 }
}
接口隔离原则
根据接口隔离原则,你应该构建小而专注的接口,不强迫客户端实现他们不需要的行为。
一个简单的例子是有一个计算形状面积和体积的接口。
interface IShapeAreaCalculator{
    double calculateArea();
    double calculateVolume();
}
class Square implements IShapeAreaCalculator{
    double calculateArea(){ // 计算面积 }
    double calculateVolume(){ // 虚拟实现 }
}
问题在于,如果 Square 形状实现了这个接口,那么它被迫实现 calculateVolume() 方法,而它并不需要这个方法。另一方面,Cube 可以同时实现这两个方法。
要解决此问题,我们可以隔离接口,有两个单独的接口:一个用于计算面积,另一个用于计算体积。这将允许单个形状决定要实现什么。
interface IAreaCalculator {
    double calculateArea();
}
interface IVolumeCalculator {
    double calculateVolume();
}
class Square implements IAreaCalculator {
    @Override
    public double calculateArea() { // 计算面积 }
}
class Cube implements IAreaCalculator, IVolumeCalculator {
    @Override
    public double calculateArea() { // 计算面积 }
    @Override
    public double calculateVolume() {// 计算体积 }
}
依赖倒置原则
在依赖倒置原则中,高层模块不应该依赖低层模块。换句话说,你必须遵循抽象并确保松散耦合。
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(){
        // 通过电子邮件实现通知
    }
}
public static void main(String [] args){
    Notification notification = new EmailNotification();
    Employee employee = new Employee(notification);
    employee.notifyUser();
}
在上述示例中,我们确保了松散耦合。Employee 不依赖于任何具体实现,而是仅依赖于抽象(通知接口)。如果我们需要更改通知方式,可以创建一个新的实现并将其传递给 Employee。
结论
在本文中,我们通过示例深入剖析了SOLID原则。这些原则是构建高质量软件的关键,对软件的扩展性和可维护性意义重大。 单一职责原则让类职责单一,使代码简洁易维护。开闭原则使系统可灵活扩展,减少修改成本。里氏替换原则保障继承体系合理可靠,维持程序行为一致。接口隔离原则助于设计简洁接口,提升代码可读性。依赖倒置原则构建松散耦合关系,增强系统灵活性。 这些原则构成了开发高度可扩展和可重用应用程序的基石。
该文章在 2024/12/4 18:02:01 编辑过