简而言之
#编程 #solid #java #oop

我认为,所有软件工程师都应该理解的最重要的原理之一是坚实的原则(不是唯一的原则,而是最基本的原则)。通过理解这一原则,我们假设我们能够提供良好,更清洁和可维护的代码。

Solid是一个缩写,代表单一责任,开放式,Liskov替换,接口隔离和依赖性反演。这是使用面向对象的编程制定程序的最佳实践。

有很多文章讨论了它。但是,我认为这仍然是一个值得重新讨论的话题。让我们尝试以最简单的方式讨论这一点。本文还包括Java中的示例代码。

单一责任原则

该原则的主要目标是单个类只应对一个目的负责。这意味着,如果我们创建一个由多种目的组成的类,我们将违反这一原则。

通过遵循此原则,它将带来好处:

  • 我们的代码将更模块化。

  • 这对我们将来更容易跟踪代码,因为我们已经确定了每个类的特定目的。

错误的例子

public class Person {
    private String firstname;
    private String lastname;
    private String origin;
    private String age;
    private String sex;

    //constructor, getters and setters

    // methods that directly relate to the person properties
    public String returnFullName() {
        return firstname + " " + lastname;
    }
}

然后我们添加storeInDatabase方法。

public class Person {
    private String firstname;
    private String lastname;
    private String origin;
    private String age;
    private String sex;

    //constructor, getters and setters

    // methods that directly relate to the person properties
    public String returnFullName() {
        return firstname + " " + lastname;
    }

    public void storeInDatabase(Person person) {
        // codes that perform database action
    }
}

出了什么问题:

  • storeInDatabasePerson类无直接关系。

正确的示例

要解决此问题,我们可以将storeInDatabase方法放入新类中,假设PersonDatabase类。

public class PersonDatabase {

    public void storeInDatabase(Person person) {
        // codes that perform database action
    }
}

开放原则

该原理在 中强调了一个类别以进行扩展,但要修改

遵循此原则的好处是,我们不修改可能导致新错误的现有代码。 (但是,现有代码中的错误是一个例外,您应该修复它,而不是扩展它ð)。

例子

假设我们有下面的Car类。

public class Car {
    private String brand;
    private String color;

    // constructors, getters, and setters
}

然后,我们想要一个具有与Car类具有相同属性的Truck类,并增加了一些添加。我们可以将其扩展到下面的Truck类,而不是修改Car类。

public class Truck extends Car {
    private int cargoVolume;

    // constructors, getters, and setters
}

这样做,我们不需要修改可能已经在程序的某些部分中使用的Car类。

如果将来要创建其他类(例如Ambulance类),则可以像Truck类一样扩展Car类。

public class Ambulance extends Car {
    private hasbloodPressureGague;
    private hasThermometer;

    // constructors, getters, and setters
}

Liskov替代原则

这一原则可以说是这5个原则中最复杂的原则。 该原理的简单含义可能是一个子类,应该可以代替其基类

说,Toyota类是Car类的子类。 Toyota类应该能够替换Car类而不会破坏我们程序的行为。显示此示例:

class Car {
    private String brand;

    public Car(String brand) {
        this.brand = brand;
    }

    public void startEngine() {
        System.out.println("Engine started.");
    }

    public void stopEngine() {
        System.out.println("Engine stopped.");
    }
}

class Toyota extends Car {
    public Toyota() {
        super("Toyota");
    }

    @Override
    public void startEngine() {
        System.out.println("Toyota engine started.");
    }

    @Override
    public void stopEngine() {
        System.out.println("Toyota engine stopped.");
    }

    public void engageAutopilot() {
        System.out.println("Autopilot engaged.");
    }
}

在此Java示例中,Car类代表通用汽车,而Toyota类代表一种特定类型的汽车(Toyota Brand),它扩展了基类的行为。

我们可以互换使用Car类和Toyota类,如下所示:

public class Main {
    public static void startCar(Car car) {
        car.startEngine();
    }

    public static void stopCar(Car car) {
        car.stopEngine();
    }

    public static void engageAutopilot(Car car) {
        if (car instanceof Toyota) {
            Toyota toyota = (Toyota) car;
            toyota.engageAutopilot();
        } else {
            System.out.println("Autopilot not available for this car.");
        }
    }

    public static void main(String[] args) {
        Car myCar = new Car("SomeBrand");
        startCar(myCar);    // Output: Engine started.
        stopCar(myCar);     // Output: Engine stopped.

        Toyota myToyota = new Toyota();
        startCar(myToyota);    // Output: Toyota engine started.
        stopCar(myToyota);     // Output: Toyota engine stopped.

        engageAutopilot(myCar);     // Output: Autopilot not available for this car.
        engageAutopilot(myToyota);  // Output: Autopilot engaged.
    }
}

startCar()stopCar()方法接受Car对象作为参数。我们可以将Car实例(myCar)和Toyota实例(myToyota)传递给这些方法。

engageAutopilot()方法检查提供的汽车对象是否是Toyota的实例。如果是这样,它将其施放到Toyota对象并调用engageAutopilot()方法。否则,它将处理自动驾驶功能不适合非Toyota车的情况。这证明了Toyota类的用法是Car类的子类型,而无需违反Liskov替代原则。

界面隔离原理

隔离意味着将不同的事物分开。该原理是关于基于其目的分开界面的。

原则指出,许多客户特定的接口比一个通用接口更好。客户不应被迫实施他们不需要的功能。

interface Worker {
    void work();
    void sleep();
}

假设我们有2个类来实现Worker接口,ProgrammerRobot类。

class Programmer implements Worker {
    public void work() {
        System.out.println("Programmer is working.");
        // Perform programming tasks
    }

    public void sleep() {
        System.out.println("Programmer is sleeping.");
        // Sleep at night
    }
}

class Robot implements Worker {
    public void work() {
        System.out.println("Robot is working.");
        // Perform robotic tasks
    }

    // Can't be implemented
    public void sleep() {
        // Robots don't sleep!
        throw new UnsupportedOperationException("Robots don't sleep!");
    }
}

正如我们看到的,Sheet()方法可以由程序员类实现,但不能由Robot类实现。为了解决这个问题,我们可以将Worker接口分开。

interface Worker {
    void work();
}

interface LifeBeing {
    void sleep();
}

Programmer类现在实现所有两个接口,因为它执行了所有相关的操作。但是,Robot类仅实现Worker接口,因为它不需要睡觉。

class Programmer implements Worker, LifeBeing {
    public void work() {
        System.out.println("Programmer is working.");
        // Perform programming tasks
    }

    public void sleep() {
        System.out.println("Programmer is sleeping.");
        // Sleep at night
    }
}

class Robot implements Worker {
    public void work() {
        System.out.println("Robot is working.");
        // Perform robotic tasks
    }
}

依赖性反转原理

该原理是指软件模块的解耦。依赖性反转是面向对象的编程中的设计原理,该原理指出高级模块不应取决于低级模块。相反,两者都应取决于抽象。

// High-level module
class EmployeeTracker {
    private EmployeeService employeeService;

    public WeatherTracker(EmployeeService employeeService) {
        this.employeeService = employeeService;
    }

    public String getCurrentWeather() {
        int zipCode = 12345;
        return weatherService.getWeather(zipCode);
    }
}

// Abstraction (interface) for employee service
interface EmployeeService {
    String getBranch(String employeeId);
    String getWage(String employeeId);
    String getPosition(String employeeId);
}

// Low-level module implementing the employee service
class EmployeeServiceImpl implements EmployeeService {
    public String getBranch(String employeeId) {
        // Logic to retrieve employee data
        return employee.getBranch();
    }
    public String getWage(String employeeId) {
        // Logic to retrieve employee data
        return employee.getWage();
    }
    public String getPosition(String employeeId) {
        // Logic to retrieve employee data
        return employee.getPosition();
    }
}

称为EmployeeTracker的高级模块需要检索员工数据。它不是直接取决于特定的实现,而取决于EmployeeService抽象/接口。

EmployeeServiceImpl类是一个实现EmployeeService接口的低级模块。它包含用于检索员工数据的特定实现详细信息,例如拨打API调用。

EmployeeTracker类不取决于这些实现细节,促进松散的耦合并确保高级模块不会与特定的低级模块紧密耦合。

通过应用此原理,我们将高级和低级模块之间的依赖关系关系倒置。高级模块取决于抽象,而低级模块取决于这些抽象。这促进了模块化和灵活的代码设计

结论

这都是对扎实原则的简短解释。您可以通过将这些原则应用于代码来提高代码质量。遵循以下原则,我们将获得一些好处:

  • 可维护性:更轻松的代码维护和修改。

  • 可伸缩性:能够添加新功能而无需修改现有代码。

  • 可检验性:促进有效的单元测试和代码验证。

  • 可重用性:可重复使用和可互换组件的创建。

  • 灵活性:更大的适应性和切换实现的能力。

  • 可读性和可理解性:清晰,有组织的代码结构,更容易理解和协作。

其他资源