设计模式中的五大基本原则,也称为SOLID原则,是面向对象设计的核心原则,它们帮助开发人员构建可扩展、可维护的系统。SOLID原则由罗伯特·C·马丁(Robert C. Martin)总结,这些原则的名称来自每个原则的首字母。它们分别是:
- 单一职责原则(Single Responsibility Principle,SRP)
- 开闭原则(Open/Closed Principle,OCP)
- 里氏替换原则(Liskov Substitution Principle,LSP)
- 接口隔离原则(Interface Segregation Principle,ISP)
- 依赖倒置原则(Dependency Inversion Principle,DIP)
1. 单一职责原则(SRP)
定义:一个类应该只有一个导致它变化的原因。
解释:每个类只应负责一项职责,或者说,一个类只应该有一个引起它变化的原因。职责越多,类的修改可能性越大,容易产生耦合问题。
例子 :
假设有一个 Employee
类负责员工信息存储和工资计算。如果未来业务需求变化,只需要修改工资计算的部分,但由于该类还包含其他职责,可能会无意中引入错误。
解决方案 :
将 Employee
类拆分为两个类,一个负责员工信息管理,一个负责工资计算。
kotlin
class Employee {
private String name;
// 仅负责员工信息管理
}
class SalaryCalculator {
// 负责工资计算
}
2. 开闭原则(OCP)
定义:软件实体应该对扩展开放,对修改关闭。
解释 :开闭原则的核心是通过扩展 而不是修改已有代码来应对需求的变化。换句话说,当我们需要新增功能时,应该通过新增代码(如新的类或模块),而不是修改已有代码。
例子 :
假设你有一个图形绘制系统,最初只能绘制圆形,现在需要扩展为绘制矩形。如果你直接修改原有代码,可能会引发新的问题。
解决方案 :
通过抽象类或接口来实现开闭原则,避免修改已有的绘图逻辑。
csharp
interface Shape {
void draw();
}
class Circle implements Shape {
public void draw() {
// 绘制圆形
}
}
class Rectangle implements Shape {
public void draw() {
// 绘制矩形
}
}
3. 里氏替换原则(LSP)
定义:派生类必须能够替换其基类,而不影响程序的正确性。
解释:子类可以替换父类并且保证程序行为不变。如果在使用基类的地方不能透明地替换为子类,那就违反了里氏替换原则。
例子 :
假设你有一个父类 Bird
,它包含一个 fly
方法。然后你创建了一个子类 Penguin
,但企鹅不能飞。如果你把企鹅放入需要飞行的地方,系统行为就会异常,违反了里氏替换原则。
解决方案 :
使用更具体的抽象,比如创建 FlyingBird
类,而不是让所有鸟类都继承 fly
方法。
scala
class Bird {
// 一般鸟类的属性和方法
}
class FlyingBird extends Bird {
void fly() {
// 具有飞行能力的鸟类
}
}
class Penguin extends Bird {
// 企鹅不具有飞行能力
}
4. 接口隔离原则(ISP)
定义:不应该强迫一个类实现它用不到的接口。
解释:与其让类实现一个庞大的接口,不如将接口拆分为多个更小、更具体的接口。这样,类只需要依赖它实际需要的接口方法。
例子 :
假设有一个 Worker
接口,包含 work()
和 eat()
方法。如果一个机器人类需要实现 Worker
接口,它可能不需要 eat()
方法。
解决方案 :
将接口分解为更小的接口,让类按需实现它们。
csharp
interface Workable {
void work();
}
interface Eatable {
void eat();
}
class Robot implements Workable {
public void work() {
// 机器人工作
}
}
class Human implements Workable, Eatable {
public void work() {
// 人类工作
}
public void eat() {
// 人类进食
}
}
5. 依赖倒置原则(DIP)
定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象。抽象不应该依赖细节,细节应该依赖抽象。
解释:这个原则的重点是通过依赖抽象(接口或抽象类)来降低耦合度,而不是直接依赖具体实现。依赖倒置使系统更具灵活性和扩展性。
例子 :
假设你有一个 Button
类,它直接依赖于 Light
类来打开灯,这样会导致高层模块(Button
)依赖于低层模块(Light
)。
解决方案 :
通过依赖抽象,来降低类之间的耦合度。
csharp
interface Switchable {
void turnOn();
}
class Light implements Switchable {
public void turnOn() {
System.out.println("Light turned on");
}
}
class Button {
private Switchable device;
public Button(Switchable device) {
this.device = device;
}
public void press() {
device.turnOn();
}
}
通过依赖接口 Switchable
,Button
类不再直接依赖 Light
类。这样,Button
可以用来控制任何实现了 Switchable
接口的设备。
总结
- 单一职责原则:一个类应只有一个引起变化的原因。
- 开闭原则:类应对扩展开放,对修改关闭。
- 里氏替换原则:子类可以替代父类而不影响系统的行为。
- 接口隔离原则:应提供小而专一的接口,避免依赖多余的方法。
- 依赖倒置原则:高层模块应依赖抽象,细节模块也应依赖抽象。