@TOC
概述
设计模式的六大原则是面向对象设计的基石,遵循这些原则可以提升代码的可维护性、可扩展性和可读性。下面对六大原则进行详细解析。
一、单一职责原则 (Single Responsibility Principle, SRP)
1. 定义
一个类应该只有一个引起变化的原因,即:一个类只负责一项职责。
2. 核心思想
- 高内聚:类的功能要集中
- 低耦合:类之间的依赖要少
3. 代码示例
❌ 违反 SRP 的例子
java
// 违反单一职责原则
public class UserService {
public void registerUser(String username, String password) {
if (username == null || username.isEmpty()) {
throw new IllegalArgumentException("用户名不能为空");
}
if (password == null || password.length() < 6) {
throw new IllegalArgumentException("密码长度不能少于6位");
}
saveUserToDatabase(username, password);
sendWelcomeEmail(username);
}
private void saveUserToDatabase(String username, String password) {
System.out.println("用户保存到数据库: " + username);
}
private void sendWelcomeEmail(String username) {
System.out.println("发送欢迎邮件给: " + username);
}
}
问题:
UserService同时承担了验证、持久化、邮件发送三种职责,职责不单一。
✅ 遵循 SRP 的改进
java
public class User {
private String username;
private String password;
// getter/setter
}
// 只负责验证
public class UserValidator {
public void validateUser(User user) {
if (user.getUsername() == null || user.getUsername().isEmpty()) {
throw new IllegalArgumentException("用户名不能为空");
}
if (user.getPassword() == null || user.getPassword().length() < 6) {
throw new IllegalArgumentException("密码长度不能少于6位");
}
}
}
// 只负责数据持久化
public class UserRepository {
public void saveUser(User user) {
System.out.println("用户保存到数据库: " + user.getUsername());
}
}
// 只负责邮件发送
public class EmailService {
public void sendWelcomeEmail(String username) {
System.out.println("发送欢迎邮件给: " + username);
}
}
// 协调各服务
public class UserService {
private UserValidator validator;
private UserRepository repository;
private EmailService emailService;
public UserService() {
this.validator = new UserValidator();
this.repository = new UserRepository();
this.emailService = new EmailService();
}
public void registerUser(User user) {
validator.validateUser(user);
repository.saveUser(user);
emailService.sendWelcomeEmail(user.getUsername());
}
}
4. 优点
- 降低类的复杂度
- 提高类的可读性
- 提高系统的可维护性
- 降低变更引起的风险
二、开闭原则 (Open-Closed Principle, OCP)
1. 定义
软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
2. 核心思想
- 通过抽象和接口实现扩展性
- 新增功能时不需要修改现有代码
3. 代码示例
❌ 违反 OCP 的例子
java
public class ShapeCalculator {
public double calculateArea(String shapeType, double... params) {
if ("circle".equals(shapeType)) {
return Math.PI * params[0] * params[0];
} else if ("rectangle".equals(shapeType)) {
return params[0] * params[1];
} else if ("triangle".equals(shapeType)) {
return params[0] * params[1] / 2;
}
throw new IllegalArgumentException("不支持的形状类型");
}
}
问题:每新增一种图形,都需要修改
calculateArea方法,违反了"对修改关闭"。
✅ 遵循 OCP 的改进
java
public interface Shape {
double calculateArea();
}
public class Circle implements Shape {
private double radius;
public Circle(double radius) { this.radius = radius; }
@Override public double calculateArea() {
return Math.PI * radius * radius;
}
}
public class Rectangle implements Shape {
private double width, height;
public Rectangle(double width, double height) {
this.width = width; this.height = height;
}
@Override public double calculateArea() {
return width * height;
}
}
public class Triangle implements Shape {
private double base, height;
public Triangle(double base, double height) {
this.base = base; this.height = height;
}
@Override public double calculateArea() {
return base * height / 2;
}
}
// 扩展无需修改
public class Ellipse implements Shape {
private double a, b;
public Ellipse(double a, double b) { this.a = a; this.b = b; }
@Override public double calculateArea() {
return Math.PI * a * b;
}
}
// 面积计算器:对扩展开放,对修改关闭
public class AreaCalculator {
public double calculateTotalArea(List<Shape> shapes) {
return shapes.stream().mapToDouble(Shape::calculateArea).sum();
}
}
4. 优点
- 提高代码的可扩展性
- 使软件更易于维护
- 提高代码的稳定性
三、里氏替换原则 (Liskov Substitution Principle, LSP)
1. 定义
所有引用基类的地方必须能透明地使用其子类的对象。
2. 核心思想
- 子类可以扩展父类功能,但不能改变原有功能
- 子类不应重写父类的非抽象方法,破坏行为一致性
3. 代码示例
❌ 违反 LSP 的例子
java
class Rectangle {
protected double width, height;
public void setWidth(double width) { this.width = width; }
public void setHeight(double height) { this.height = height; }
public double getArea() { return width * height; }
}
class Square extends Rectangle {
@Override
public void setWidth(double width) {
super.setWidth(width);
super.setHeight(width); // 强制同步
}
@Override
public void setHeight(double height) {
super.setWidth(height);
super.setHeight(height);
}
}
// 测试
public class Test {
public static void main(String[] args) {
Rectangle rect = new Square();
rect.setWidth(5);
rect.setHeight(10);
System.out.println("面积: " + rect.getArea()); // 输出 100,期望 50
}
}
问题:子类改变了父类的行为,导致多态失效。
✅ 遵循 LSP 的改进
java
abstract class Shape {
public abstract double getArea();
}
class Rectangle extends Shape {
private double width, height;
public Rectangle(double width, double height) {
this.width = width; this.height = height;
}
@Override public double getArea() { return width * height; }
}
class Square extends Shape {
private double side;
public Square(double side) { this.side = side; }
@Override public double getArea() { return side * side; }
}
// 使用工厂创建
class ShapeFactory {
public static Shape createRectangle(double w, double h) {
return new Rectangle(w, h);
}
public static Shape createSquare(double s) {
return new Square(s);
}
}
4. 优点
- 保证继承关系的正确性
- 提高代码健壮性
- 增强程序可靠性
四、接口隔离原则 (Interface Segregation Principle, ISP)
1. 定义
客户端不应该依赖它不需要的接口,一个类对另一个类的依赖应建立在最小接口上。
2. 核心思想
- 接口要小而专,避免"胖接口"
- 为不同客户端提供专用接口
3. 代码示例
❌ 违反 ISP 的例子
java
interface Animal {
void eat();
void sleep();
void fly(); // 并非所有动物都会飞
void swim(); // 并非所有动物都会游泳
}
class Bird implements Animal {
public void eat() { System.out.println("鸟在吃东西"); }
public void sleep() { System.out.println("鸟在睡觉"); }
public void fly() { System.out.println("鸟在飞翔"); }
public void swim() { throw new UnsupportedOperationException("鸟不会游泳"); }
}
class Fish implements Animal {
public void eat() { System.out.println("鱼在吃东西"); }
public void sleep() { System.out.println("鱼在睡觉"); }
public void fly() { throw new UnsupportedOperationException("鱼不会飞"); }
public void swim() { System.out.println("鱼在游泳"); }
}
问题:实现类被迫实现无意义的方法。
✅ 遵循 ISP 的改进
java
interface BasicAnimal { void eat(); void sleep(); }
interface Flyable { void fly(); }
interface Swimmable { void swim(); }
interface Runnable { void run(); }
class Bird implements BasicAnimal, Flyable {
public void eat() { System.out.println("鸟在吃东西"); }
public void sleep() { System.out.println("鸟在睡觉"); }
public void fly() { System.out.println("鸟在飞翔"); }
}
class Fish implements BasicAnimal, Swimmable {
public void eat() { System.out.println("鱼在吃东西"); }
public void sleep() { System.out.println("鱼在睡觉"); }
public void swim() { System.out.println("鱼在游泳"); }
}
class Duck implements BasicAnimal, Flyable, Swimmable, Runnable {
public void eat() { System.out.println("鸭子在吃东西"); }
public void sleep() { System.out.println("鸭子在睡觉"); }
public void fly() { System.out.println("鸭子在飞翔"); }
public void swim() { System.out.println("鸭子在游泳"); }
public void run() { System.out.println("鸭子在奔跑"); }
}
4.优点
- 降低接口复杂度
- 提高系统灵活性
- 保证接口纯洁性
五、依赖倒置原则 (Dependency Inversion Principle, DIP)
1. 定义
- 高层模块不应依赖低层模块,二者都应依赖抽象
- 抽象不应依赖细节,细节应依赖抽象
2. 核心思想
- 面向接口编程,而非面向实现
- 使用依赖注入实现解耦
3. 代码示例
❌ 违反 DIP 的例子
java
class MySQLDatabase {
public void connect() { System.out.println("连接MySQL数据库"); }
public void query(String sql) { System.out.println("执行MySQL查询: " + sql); }
}
class UserService {
private MySQLDatabase database = new MySQLDatabase(); // 紧耦合
public void getUserById(int id) {
database.connect();
database.query("SELECT * FROM users WHERE id = " + id);
}
}
问题:
UserService直接依赖具体实现,难以替换数据库。
✅ 遵循 DIP 的改进
java
interface Database {
void connect();
void query(String sql);
}
class MySQLDatabase implements Database {
@Override public void connect() { System.out.println("连接MySQL数据库"); }
@Override public void query(String sql) { System.out.println("执行MySQL查询: " + sql); }
}
class PostgreSQLDatabase implements Database {
@Override public void connect() { System.out.println("连接PostgreSQL数据库"); }
@Override public void query(String sql) { System.out.println("执行PostgreSQL查询: " + sql); }
}
class UserService {
private Database database;
public UserService(Database database) { // 依赖注入
this.database = database;
}
public void getUserById(int id) {
database.connect();
database.query("SELECT * FROM users WHERE id = " + id);
}
}
// 使用示例
public class DIPExample {
public static void main(String[] args) {
Database mysql = new MySQLDatabase();
Database postgresql = new PostgreSQLDatabase();
UserService userService1 = new UserService(mysql);
userService1.getUserById(1);
UserService userService2 = new UserService(postgresql);
userService2.getUserById(1);
}
}
4. 优点
- 降低类间耦合度
- 提高系统稳定性
- 增强可维护性和可测试性
六、迪米特法则 (Law of Demeter, LoD) / 最少知识原则
1. 定义
一个对象应该对其他对象保持最少的了解,只与直接朋友通信。
2. 核心思想
- 降低类之间的耦合
- 提高模块独立性
2. 代码示例
❌ 违反迪米特法则的例子
java
class Company {
private Employee manager;
public Employee getManager() { return manager; }
}
class Employee {
private Department department;
public Department getDepartment() { return department; }
}
class Department {
private String address;
public String getAddress() { return address; }
}
class Client {
public void printCompanyAddress(Company company) {
// 违反:链式调用,了解过多内部结构
System.out.println(company.getManager().getDepartment().getAddress());
}
}
问题:客户端需了解
Company→Employee→Department的链式结构。
✅ 遵循迪米特法则的改进
java
class Company {
private Employee manager;
public String getCompanyAddress() {
return manager.getDepartmentAddress(); // 隐藏细节
}
}
class Employee {
private Department department;
public String getDepartmentAddress() {
return department.getAddress();
}
}
class Department {
private String address;
public String getAddress() { return address; }
}
class Client {
public void printCompanyAddress(Company company) {
// 只与 Company 通信
System.out.println(company.getCompanyAddress());
}
}
更完整的例子:电脑启动
java
class Computer {
private CPU cpu;
private Memory memory;
private HardDisk hardDisk;
public Computer(CPU cpu, Memory memory, HardDisk hardDisk) {
this.cpu = cpu;
this.memory = memory;
this.hardDisk = hardDisk;
}
public void start() {
cpu.start();
memory.check();
hardDisk.read();
System.out.println("电脑启动成功");
}
public void shutdown() {
cpu.shutdown();
memory.clear();
hardDisk.write();
System.out.println("电脑关闭成功");
}
}
// 用户只需调用 Computer,无需了解内部组件
class User {
public void useComputer() {
Computer computer = new Computer(new CPU(), new Memory(), new HardDisk());
computer.start();
computer.shutdown();
}
}
4. 优点
- 降低类之间的耦合度
- 提高模块的相对独立性
- 提高系统的可维护性
七、六大原则总结
| 原则 | 核心思想 | 关键点 |
|---|---|---|
| 单一职责原则 (SRP) | 一个类只负责一项职责 | 高内聚、低耦合 |
| 开闭原则 (OCP) | 对扩展开放,对修改关闭 | 抽象、多态、接口 |
| 里氏替换原则 (LSP) | 子类可替换父类 | 继承的正确使用 |
| 接口隔离原则 (ISP) | 使用多个专门的接口 | 接口细化、避免冗余 |
| 依赖倒置原则 (DIP) | 依赖抽象而非实现 | 面向接口编程、依赖注入 |
| 迪米特法则 (LoD) | 最少知识原则 | 降低耦合、封装细节 |
八、综合应用示例
java
// 1. 单一职责 + 接口隔离
interface Payment { void pay(double amount); }
interface Refund { void refund(double amount); }
// 2. 开闭原则 + 依赖倒置
class PaymentProcessor {
private Payment payment;
public PaymentProcessor(Payment payment) {
this.payment = payment;
}
public void processPayment(double amount) {
payment.pay(amount);
}
}
// 3. 里氏替换原则
class CreditCardPayment implements Payment, Refund {
public void pay(double amount) { System.out.println("信用卡支付: " + amount); }
public void refund(double amount) { System.out.println("信用卡退款: " + amount); }
}
class PayPalPayment implements Payment {
public void pay(double amount) { System.out.println("PayPal支付: " + amount); }
}
// 4. 迪米特法则
class OrderService {
private PaymentProcessor paymentProcessor;
public OrderService(PaymentProcessor paymentProcessor) {
this.paymentProcessor = paymentProcessor;
}
public void checkout(double amount) {
paymentProcessor.processPayment(amount); // 客户端无需知道细节
}
}
总结 :
这六大原则是构建高质量、可维护、可扩展软件系统的基础。在实际开发中,应灵活运用这些原则,避免过度设计,做到"恰到好处的抽象"。
💡 提示:原则是指导,不是教条。结合业务场景合理使用才是关键。