六大原则
面向对象设计的六大原则
面向对象设计(OOD)的六大原则是软件设计的基石,旨在提高代码的可维护性、可扩展性和复用性。这些原则相互关联,共同指导开发者构建灵活、健壮的系统。
开闭原则(Open-Closed Principle, OCP)
核心思想
对扩展开放,对修改关闭。
即软件实体(类、模块、接口等)应允许通过扩展新增功能,而无需修改原有代码。
实现方式
- 通过抽象基类或接口定义稳定的核心逻辑,具体实现延迟到子类。
- 新增功能时,只需添加新的子类或实现类,而非修改现有代码。
示例
// 抽象图形接口(稳定)
interface Shape {
double area();
}
// 现有实现(无需修改)
class Circle implements Shape {
private double radius;
public double area() { return Math.PI * radius * radius; }
}
// 扩展新功能(新增类,不修改原有代码)
class Rectangle implements Shape {
private double width, height;
public double area() { return width * height; }
}
优势
- 减少修改原有代码带来的风险(如引入新 bug)。
- 提高系统的适应性和可扩展性。
里氏代换原则(Liskov Substitution Principle, LSP)
核心思想
子类可以替换父类出现的任何地方,且替换后不会改变原有程序的正确性。
即子类必须完全遵守父类的行为契约,不能破坏父类的功能逻辑。
违反场景
- 子类重写父类方法时,改变了方法的预期行为(如返回值范围、异常抛出)。
- 子类新增父类未声明的约束(如参数校验更严格)。
示例
// 父类:矩形
class Rectangle {
protected int width, height;
public void setWidth(int w) { width = w; }
public void setHeight(int h) { height = h; }
public int area() { return width * height; }
}
// 子类:正方形(符合里氏代换)
class Square extends Rectangle {
public void setWidth(int w) {
super.setWidth(w);
super.setHeight(w); // 保持宽高相等,不破坏父类逻辑
}
public void setHeight(int h) {
super.setWidth(h);
super.setHeight(h);
}
}
优势
- 保证继承关系的合理性,避免因子类替换导致的逻辑错误。
- 增强代码的可替换性,便于测试和扩展。
依赖倒转原则(Dependency Inversion Principle, DIP)
核心思想
依赖于抽象,而非具体实现。
即高层模块不应依赖低层模块,两者都应依赖抽象;抽象不应依赖细节,细节应依赖抽象。
实现方式
- 通过接口或抽象类定义交互契约,高层模块调用抽象接口。
- 低层模块实现抽象接口,使高层模块与具体实现解耦。
示例
// 抽象接口(高层与低层共同依赖)
interface MessageSender {
void send(String message);
}
// 低层实现(依赖抽象)
class EmailSender implements MessageSender {
public void send(String message) { /* 发送邮件 */ }
}
// 高层模块(依赖抽象,不依赖具体实现)
class NotificationService {
private MessageSender sender;
// 构造注入抽象接口
public NotificationService(MessageSender sender) {
this.sender = sender;
}
public void notify(String message) {
sender.send(message); // 调用抽象方法
}
}
优势
- 降低模块间的耦合度,便于替换低层实现(如从邮件发送改为短信发送)。
- 提高系统的灵活性和可维护性。
接口隔离原则(Interface Segregation Principle, ISP)
核心思想
使用多个专用接口,而非一个通用接口。
即接口应最小化,每个接口只包含客户端所需的方法,避免客户端依赖不需要的方法。
违反场景
- 一个接口包含过多方法,导致实现类被迫实现无关方法。
- 客户端依赖于其不使用的方法,增加耦合度。
示例
// 错误:通用接口包含过多方法
interface Worker {
void work();
void eat();
void sleep();
}
// 正确:拆分多个专用接口
interface Workable { void work(); }
interface Eatable { void eat(); }
interface Sleepable { void sleep(); }
// 实现类按需实现接口
class Human implements Workable, Eatable, Sleepable {
public void work() { /* ... */ }
public void eat() { /* ... */ }
public void sleep() { /* ... */ }
}
class Robot implements Workable { // 机器人无需eat和sleep
public void work() { /* ... */ }
}
优势
- 减少接口冗余,降低实现类的负担。
- 避免客户端与无关方法的耦合,提高系统灵活性。
迪米特原则(Law of Demeter, LoD)
核心思想
一个实体应尽可能少地与其他实体发生交互(又称 “最少知识原则”)。
即对象只应与直接朋友(成员变量、方法参数、返回值)通信,避免访问 “朋友的朋友”。
实现方式
- 减少对象之间的直接依赖,通过中间层(如门面模式)隔离交互。
- 隐藏内部实现细节,只暴露必要的公共方法。
示例
// 违反:直接访问朋友的朋友
class Customer {
void checkOrderStatus(Order order) {
// 直接访问Order的内部对象(ShippingDetails)
String status = order.getShippingDetails().getStatus();
System.out.println(status);
}
}
// 正确:通过朋友(Order)间接访问
class Customer {
void checkOrderStatus(Order order) {
// 调用Order的方法,而非直接访问其内部对象
String status = order.getShippingStatus();
System.out.println(status);
}
}
class Order {
private ShippingDetails shipping;
// 封装内部细节,提供对外方法
public String getShippingStatus() {
return shipping.getStatus();
}
}
优势
- 降低系统复杂度,减少对象间的耦合。
- 提高代码的可维护性,避免因一个对象的修改影响多个关联对象。
单一职责原则(Single Responsibility Principle, SRP)
核心思想
一个类或接口只负责一项职责。
即一个类应只有一个引起它变化的原因,避免职责混杂导致的维护困难。
违反场景
- 一个类同时处理数据存储、业务逻辑和 UI 展示。
- 方法实现多个不相关的功能(如同时处理计算和日志输出)。
示例
// 违反:职责混杂(计算+日志)
class Calculator {
public int add(int a, int b) {
int result = a + b;
// 包含日志功能,违反单一职责
logToFile("Result: " + result);
return result;
}
private void logToFile(String message) { /* ... */ }
}
// 正确:拆分职责
class Calculator { // 仅负责计算
public int add(int a, int b) { return a + b; }
}
class Logger { // 仅负责日志
public void log(String message) { /* ... */ }
}
// 客户端组合使用
class Client {
public void calculateAndLog() {
Calculator calc = new Calculator();
Logger logger = new Logger();
int result = calc.add(1, 2);
logger.log("Result: " + result);
}
}
优势
- 提高类的内聚性,使代码更简洁、易懂。
- 降低修改风险,一个职责的变化不会影响其他职责。
总结
六大原则的核心目标是降低耦合、提高内聚,构建可扩展、可维护的系统:
- 开闭原则:扩展优先于修改;
- 里氏代换:子类替换不影响正确性;
- 依赖倒转:面向抽象编程;
- 接口隔离:接口最小化;
- 迪米特原则:减少交互,弱耦合;
- 单一职责:职责明确,降低复杂度