面向对象设计的六大原则
面向对象设计(OOD)的六大原则是软件设计的基石,旨在提高代码的可维护性、可扩展性和复用性。这些原则相互关联,共同指导开发者构建灵活、健壮的系统。
开闭原则(Open-Closed Principle, OCP)
核心思想
对扩展开放,对修改关闭。
即软件实体(类、模块、接口等)应允许通过扩展新增功能,而无需修改原有代码。
实现方式
- 通过抽象基类或接口定义稳定的核心逻辑,具体实现延迟到子类。
- 新增功能时,只需添加新的子类或实现类,而非修改现有代码。
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| interface Shape { double area(); }
class Circle implements Shape { private double radius; @Override public double area() { return Math.PI * radius * radius; } }
class Rectangle implements Shape { private double width, height; @Override public double area() { return width * height; } }
|
优势
- 减少修改原有代码带来的风险(如引入新 bug)。
- 提高系统的适应性和可扩展性。
里氏代换原则(Liskov Substitution Principle, LSP)
核心思想
子类可以替换父类出现的任何地方,且替换后不会改变原有程序的正确性。
即子类必须完全遵守父类的行为契约,不能破坏父类的功能逻辑。
违反场景
- 子类重写父类方法时,改变了方法的预期行为(如返回值范围、异常抛出)。
- 子类新增父类未声明的约束(如参数校验更严格)。
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 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 { @Override public void setWidth(int w) { super.setWidth(w); super.setHeight(w); } @Override public void setHeight(int h) { super.setWidth(h); super.setHeight(h); } }
|
优势
- 保证继承关系的合理性,避免因子类替换导致的逻辑错误。
- 增强代码的可替换性,便于测试和扩展。
依赖倒转原则(Dependency Inversion Principle, DIP)
核心思想
依赖于抽象,而非具体实现。
即高层模块不应依赖低层模块,两者都应依赖抽象;抽象不应依赖细节,细节应依赖抽象。
实现方式
- 通过接口或抽象类定义交互契约,高层模块调用抽象接口。
- 低层模块实现抽象接口,使高层模块与具体实现解耦。
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| interface MessageSender { void send(String message); }
class EmailSender implements MessageSender { @Override 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)
核心思想
使用多个专用接口,而非一个通用接口。
即接口应最小化,每个接口只包含客户端所需的方法,避免客户端依赖不需要的方法。
违反场景
- 一个接口包含过多方法,导致实现类被迫实现无关方法。
- 客户端依赖于其不使用的方法,增加耦合度。
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 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 { @Override public void work() { } @Override public void eat() { } @Override public void sleep() { } }
class Robot implements Workable { @Override public void work() { } }
|
优势
- 减少接口冗余,降低实现类的负担。
- 避免客户端与无关方法的耦合,提高系统灵活性。
迪米特原则(Law of Demeter, LoD)
核心思想
一个实体应尽可能少地与其他实体发生交互(又称 “最少知识原则”)。
即对象只应与直接朋友(成员变量、方法参数、返回值)通信,避免访问 “朋友的朋友”。
实现方式
- 减少对象之间的直接依赖,通过中间层(如门面模式)隔离交互。
- 隐藏内部实现细节,只暴露必要的公共方法。
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class Customer { void checkOrderStatus(Order order) { String status = order.getShippingDetails().getStatus(); System.out.println(status); } }
class Customer { void checkOrderStatus(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 展示。
- 方法实现多个不相关的功能(如同时处理计算和日志输出)。
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| 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); } }
|
优势
- 提高类的内聚性,使代码更简洁、易懂。
- 降低修改风险,一个职责的变化不会影响其他职责。
总结
六大原则的核心目标是降低耦合、提高内聚,构建可扩展、可维护的系统:
- 开闭原则:扩展优先于修改;
- 里氏代换:子类替换不影响正确性;
- 依赖倒转:面向抽象编程;
- 接口隔离:接口最小化;
- 迪米特原则:减少交互,弱耦合;
- 单一职责:职责明确,降低复杂度
v1.3.10