我认为,所有软件工程师都应该理解的最重要的原理之一是坚实的原则(不是唯一的原则,而是最基本的原则)。通过理解这一原则,我们假设我们能够提供良好,更清洁和可维护的代码。
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
}
}
出了什么问题:
-
storeInDatabase
与Person
类无直接关系。
正确的示例
要解决此问题,我们可以将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
接口,Programmer
和Robot
类。
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
类不取决于这些实现细节,促进松散的耦合并确保高级模块不会与特定的低级模块紧密耦合。
通过应用此原理,我们将高级和低级模块之间的依赖关系关系倒置。高级模块取决于抽象,而低级模块取决于这些抽象。这促进了模块化和灵活的代码设计。
结论
这都是对扎实原则的简短解释。您可以通过将这些原则应用于代码来提高代码质量。遵循以下原则,我们将获得一些好处:
-
可维护性:更轻松的代码维护和修改。
-
可伸缩性:能够添加新功能而无需修改现有代码。
-
可检验性:促进有效的单元测试和代码验证。
-
可重用性:可重复使用和可互换组件的创建。
-
灵活性:更大的适应性和切换实现的能力。
-
可读性和可理解性:清晰,有组织的代码结构,更容易理解和协作。