SOLID 原则基本上构成了构建面向对象、松散耦合、健壮、可维护和易于理解的应用程序的基本准则。最常被问到的面试问题之一,让我们来看看:
单一职责:
一个类应该有且只有一个职责。我们应该仅仅为了一个目的而编写、更改或维护一个类,这给我们带来了更清晰、健壮的代码和更少的测试用例的优势,因为我们知道哪个类涵盖了哪些功能以及在更改的情况下将修改哪个类。例如:
public class Vehicle{ public void printDetails() {} public double calculate() {} public void addtoDB() {} } |
上面的类有三个独立的职责:打印、计算和添加到数据库。通过应用SRP,我们可以将上面的类分成三个不同职责的类。
Open Closed:
一个类应该对扩展开放,对修改关闭,这意味着我们不希望修改现有代码导致潜在的问题或错误。其他开发人员在某些功能更改的情况下应该能够扩展我们的类并覆盖某些方法。例如:如果我们想再添加一种类型,比如电动自行车,我们将不得不用一个违反原则的 if 语句修改上面的类。更好的方法是让子类根据它们的类型覆盖 Value 方法。
public class VehiclePrice { public double Value(Vehicle v) { if (v instanceof Car) { return v.getValue() * 0.5; if (v instanceof Truck) { return v.getValue() * 0.8; } } |
Liskov 替换:
派生类必须可以替换基类。简单来说,如果类 A 扩展了类 B,我们应该能够用 A 替换 B,而不会破坏我们程序的行为。根据 OOPS 概念,继承是一种很好的做法,但 Liskov 原则要求谨慎考虑使用继承。如果我们的超类在所有实例中都可以替换为子类,我们应该只使用继承。
例如:经典方形矩形问题。由于其明确的基类 Rectangle 显然不能被子类 Square 替换,因为 square 具有相等的高度和宽度的约束,因此用 Square 类替换 Rectangle 类会破坏 LSP。
public class Rectangle { private double height; private double width; public void setHeight(double h) { height = h; } public void setWidht(double w) { width = w; } //getters and calculateArea method } public class Square extends Rectangle { public void setHeight(double h) { super.setHeight(h); super.setWidth(w); } public void setWidth(double h) { super.setHeight(h); super.setWidth(w); } } |
接口隔离:
不应要求任何人在他们不会使用的类中实现方法。较大的接口应拆分为较小的接口。这确保实现类只需要关心对它们有用的方法。这使我们可以灵活地仅使用所需的功能。例如:对于自行车类实现 openDoors 方法没有意义。这应该通过将 Vehicle 接口分解为多个具有可能功能的较小接口来解决,这样就不会强制类实现非必需的方法。
public interface Vehicle { public void drive(); public void stop(); public void openDoors(); } public class Bike implements Vehicle { // Can be implemented public void drive() {...} public void stop() {...} // Can not be implemented public void openDoors() {...} } |
依赖倒置:
一个类应该依赖于抽象(接口和抽象类)而不是具体(类)。上面的代码适用于一个引擎,但是如果有两种类型的引擎,比如柴油和汽油,那将意味着修改我们的类,这不是一个好主意。这可以通过添加一个抽象层来解决,因此 car 不再直接依赖于 Engine 现在依赖于 EngineInterface。PetrolEngine 或 DieselEngine 类实现了这个接口,我们可以将它们中的任何一个连接到 Car 类:
public class Car { private Engine engine; public Car(Engine e) { engine = e; } public void start() { engine.start(); } } public class Engine { public void start() { //some implementation } } |
上面的代码将适用于一个引擎,但如果有两种类型的引擎,如柴油机和汽油机,这将意味着修改我们的类,这不是一个好主意。
这可以通过添加一个抽象层来解决,因此car不再直接依赖于Engine,而是依赖于EngineInterface。petrolegine或DieselEngine类实现了这个接口,我们可以将其中任何一个连接到Car类。