软件工程-设计原则

面向对象六大设计原则(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 { /* 仅实现玩具功能 */ }

实践规则

  1. 子类必须实现父类抽象方法,行为符合父类预期。
  2. 子类重写非抽象方法时,不能破坏父类契约。
  3. 子类可以有自己的特有方法。
  4. 若子类无法满足父类契约,应使用接口或分层设计替代继承。
    核心意义:保证多态可靠性,让代码复用更安全,提升可维护性。

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);
}
@Override
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 可扩展性 对扩展开放,对修改关闭

💡 学习建议
这些原则并非孤立存在,实际开发中往往需要组合使用。比如:

  • 先通过单一职责接口隔离拆分模块,
  • 再用依赖倒置里氏替换建立抽象关系,
  • 最后通过迪米特法则减少耦合,用开放封闭原则保证扩展性。