软件工程-设计原则
面向对象六大设计原则(SOLID+迪米特)核心:职责单一、继承规范、依赖抽象、接口精简、少知少耦合、扩展开放修改封闭,最终实现代码高内聚、低耦合、易维护、易扩展。
📚 面向对象设计六大原则(SOLID+LoD)完整解析
1. 单一职责原则 (SRP)
定义:一个类应该只有一个引起变化的原因,即只承担一项职责。
核心思想:降低类的复杂度,让职责边界清晰,避免因一个功能变更影响其他 unrelated 功能。
问题示例:1
2
3
4
5
6// ❌ 坏设计:IPhone 接口同时承担通话(dial/hangup)和数据传输(chat)职责
public interface IPhone {
void dial(String phoneNumber); // 通话
void chat(Object o); // 数据传输
void hangup(); // 通话
}
优化方案:1
2
3
4
5
6
7
8
9// ✅ 好设计:拆分接口,各自承担单一职责
public interface IConnectionManager {
void dial(String phoneNumber);
void hangup();
}
public interface IDataTransfer {
void chat(Object o);
}
class IPhone implements IConnectionManager, IDataTransfer { /* 实现 */ }
实践意义:便于单元测试、降低耦合、提高代码可维护性。
2. 里氏替换原则 (LSP)
定义:所有引用基类(父类)的地方,必须能透明地使用其子类对象,且不改变程序预期行为。
核心思想:子类必须完全实现父类的抽象方法,且行为符合父类契约;子类可以扩展功能,但不能破坏父类原有约定。
问题示例:1
2
3
4
5// ❌ 坏设计:ToyGun 继承 AbstractGun,但无法实现 shoot() 方法,破坏契约
public class ToyGun extends AbstractGun {
public void shoot() { /* 空实现,玩具枪不能射击 */ }
}
// 当 Soldier 使用 ToyGun 时,killEnemy() 会行为异常
优化方案:1
2
3// ✅ 好设计:ToyGun 脱离继承,通过关联/接口方式复用代码
public abstract class AbstractToy { /* 玩具通用功能 */ }
public class ToyGun extends AbstractToy { /* 仅实现玩具功能 */ }
实践规则:
- 子类必须实现父类抽象方法,行为符合父类预期。
- 子类重写非抽象方法时,不能破坏父类契约。
- 子类可以有自己的特有方法。
- 若子类无法满足父类契约,应使用接口或分层设计替代继承。
核心意义:保证多态可靠性,让代码复用更安全,提升可维护性。
3. 依赖倒置原则 (DIP)
定义:
- 高层模块不应该依赖低层模块,两者都应该依赖抽象。
- 抽象不应该依赖细节,细节应该依赖抽象。
核心思想:面向接口编程,通过抽象解耦高层与低层模块,让代码更易扩展。
问题示例:优化方案:1
2
3
4// ❌ 坏设计:Driver 直接依赖 Benz 类,无法灵活切换其他车型
public class Driver {
public void drive(Benz benz) { benz.run(); }
}实践意义:降低模块间耦合,方便替换实现类,提升系统弹性。1
2
3
4
5
6
7
8
9
10
11
12// ✅ 好设计:依赖 ICar 接口,可兼容任何实现 ICar 的车型
public interface IDriver {
void drive(ICar car);
}
public interface ICar {
void run();
}
public class Driver implements IDriver {
public void drive(ICar car) { car.run(); }
}
public class Benz implements ICar { /* 实现 */ }
public class BMW implements ICar { /* 实现 */ }
4. 接口隔离原则 (ISP)
定义:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应建立在最小接口上。
核心思想:避免“胖接口”,将大接口拆分为多个细粒度小接口,让客户端只依赖必要方法。
问题示例:1
2
3
4
5
6// ❌ 坏设计:IPettyGirl 接口包含所有方法,Searcher 可能只需要部分方法
public interface IPettyGirl {
void goodLooking();
void niceFigure();
void greatTemperament();
}
优化方案:1
2
3
4
5// ✅ 好设计:拆分为多个细粒度接口
public interface IGoodBodyGirl { void goodLooking(); }
public interface INiceFigure { void niceFigure(); }
public interface IGreatTemperamentGirl { void greatTemperament(); }
public class PettyGirl implements IGoodBodyGirl, INiceFigure, IGreatTemperamentGirl { /* 实现 */ }
实践意义:避免接口污染,减少不必要依赖,让代码更灵活。
5. 迪米特法则 (LoD) / 最少知识原则 (LKP)
定义:一个对象应当对其他对象尽可能少的了解,只和“直接朋友”通信。
核心思想:减少对象间交互,降低耦合,避免“牵一发而动全身”。
问题示例:1
2
3
4
5
6
7
8
9
10
11
12
13// ❌ 坏设计:InstallSoftware 直接调用 Wizard 的多个内部方法,过度了解 Wizard 细节
public class InstallSoftware {
public void installWizard(Wizard wizard) {
int first = wizard.first();
if (first > 50) {
int second = wizard.second();
if (second > 50) {
int third = wizard.third();
if (third > 50) wizard.first();
}
}
}
}
优化方案:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21// ✅ 好设计:Wizard 封装内部逻辑,InstallSoftware 只调用一个公开方法
public class Wizard {
private int first() { /* ... */ }
private int second() { /* ... */ }
private int third() { /* ... */ }
public void installWizard() {
int first = this.first();
if (first > 50) {
int second = this.second();
if (second > 50) {
int third = this.third();
if (third > 50) this.first();
}
}
}
}
public class InstallSoftware {
public void installWizard(Wizard wizard) {
wizard.installWizard(); // 仅调用公开方法,不了解内部细节
}
}
实践意义:降低类之间的耦合度,提高代码的可维护性和稳定性。
6. 开放封闭原则 (OCP)
定义:软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
核心思想:需求变更时,通过新增代码扩展功能,而非修改现有代码,避免影响已有功能。
示例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// ✅ 基础接口:IBook
public interface IBook {
String getName();
int getPrice();
String getAuthor();
}
// ✅ 实现类:NovelBook
public class NovelBook implements IBook { /* 实现 */ }
// ✅ 扩展类:OffNovelBook(新增打折功能,不修改 NovelBook)
public class OffNovelBook extends NovelBook {
public OffNovelBook(String name, int price, String author) {
super(name, price, author);
}
public int getPrice() {
int selfPrice = super.getPrice();
if (selfPrice > 4000) return selfPrice * 80 / 100; // 8折
else return selfPrice * 90 / 100; // 9折
}
}
实践意义:保证系统稳定性,便于功能迭代和复用。
📌 六大原则总结表
| 原则 | 英文缩写 | 核心关注点 | 设计目标 |
|---|---|---|---|
| 单一职责原则 | SRP | 类的职责边界 | 降低复杂度,清晰分工 |
| 里氏替换原则 | LSP | 继承契约 | 保证多态可靠,复用安全 |
| 依赖倒置原则 | DIP | 依赖方向 | 面向抽象,解耦高层与低层 |
| 接口隔离原则 | ISP | 接口粒度 | 最小依赖,避免接口污染 |
| 迪米特法则 | LoD | 对象交互 | 最少知识,降低耦合 |
| 开放封闭原则 | OCP | 可扩展性 | 对扩展开放,对修改关闭 |
💡 学习建议:
这些原则并非孤立存在,实际开发中往往需要组合使用。比如:
- 先通过单一职责和接口隔离拆分模块,
- 再用依赖倒置和里氏替换建立抽象关系,
- 最后通过迪米特法则减少耦合,用开放封闭原则保证扩展性。