面向对象设计原则

发布于:2025-03-12 ⋅ 阅读:(14) ⋅ 点赞:(0)

一、SOLID原则(核心设计原则)​

1. ​单一职责原则(Single Responsibility Principle, SRP)​

定义:一个类应该只有一个引起它变化的原因,即一个类只负责一项单一的功能。
示例:将User类拆分为User(用户信息)和UserService(用户业务逻辑)

java

// 违反SRP(混合了数据存储和业务逻辑)
class User {
    private String name;
    
    public void register() { 
        // 保存到数据库
        saveToDatabase(this); 
    }
    
    private void saveToDatabase(User user) { ... }
}

// 符合SRP
class User {
    private String name;
}

class UserService {
    public void register(User user) {
        saveToDatabase(user);
    }
    
    private void saveToDatabase(User user) { ... }
}

2. ​开放封闭原则(Open/Closed Principle, OCP)​

定义:软件实体(类、模块、函数)应该对扩展开放,对修改关闭。
示例:通过接口实现图形面积计算,新增图形无需修改现有代码

java

interface Shape {
    double area();
}

class Circle implements Shape {
    @Override
    public double area() { return Math.PI * radius * radius; }
}

class Rectangle implements Shape {
    @Override
    public double area() { return length * width; }
}

// 使用方无需修改即可扩展新形状
public class AreaCalculator {
    public static double calculateTotal(Shape[] shapes) {
        double sum = 0;
        for (Shape shape : shapes) sum += shape.area();
        return sum;
    }
}

3. ​里氏替换原则(Liskov Substitution Principle, LSP)​

定义:子类必须能够完全替代父类,子类对象出现在父类对象声明的位置时,程序的行为不应发生变化。
示例Bird继承Animal后,不应该出现Bird无法飞行的异常

java

abstract class Animal {
    public void fly() { /* 默认飞行行为 */ }
}

class Penguin extends Animal {
    @Override
    public void fly() { 
        throw new UnsupportedOperationException("Penguin can't fly!");
    }
}
// 违反LSP:Penguin无法替代Animal的fly方法

4. ​接口隔离原则(Interface Segregation Principle, ISP)​

定义:客户端不应该被迫依赖它不使用的接口,应将大接口拆分为多个小接口。
示例:将Vehicle接口拆分为DrivableSteerable

java

// 违反ISP(Car实现了不必要的接口)
interface Vehicle {
    void drive();
    void steer();
    void refuel();
}

// 符合ISP
interface Drivable {
    void drive();
}

interface Steerable {
    void steer();
}

class Car implements Drivable, Steerable {
    @Override public void drive() { /* 加速 */ }
    @Override public void steer() { /* 转向 */ }
}

5. ​依赖倒置原则(Dependency Inversion Principle, DIP)​

定义:高层模块不应该依赖低层模块,二者都应依赖抽象。抽象不应该依赖细节,细节应该依赖抽象。
示例:通过构造器注入实现依赖倒置

java

// 违反DIP(高层模块直接依赖具体实现)
class WeatherService {
    private DataSource dataSource;
    
    public WeatherService() {
        this.dataSource = new MySQLDataSource(); // 直接创建具体对象
    }
}

// 符合DIP
interface DataSource {
    String getConnection();
}

class MySQLDataSource implements DataSource { ... }
class PostgreSQLDataSource implements DataSource { ... }

class WeatherService {
    private final DataSource dataSource;
    
    public WeatherService(DataSource dataSource) {
        this.dataSource = dataSource; // 依赖抽象接口
    }
}

二、其他重要设计原则

1. ​迪米特法则(Law of Demeter, LoD)​

定义:一个对象应该对其他对象保持最少的了解,只与直接的朋友通信。
示例:避免在User类中直接调用Order类的内部方法

java

class User {
    private List<Order> orders;
    
    // 违反LoD:用户不应该知道订单的具体存储方式
    public void printOrderDetails() {
        for (Order order : orders) {
            System.out.println(order.getCustomerId()); 
            System.out.println(order.getItems());
        }
    }
}

// 改进:通过Order类提供公共接口
class Order {
    public String getCustomerId() { ... }
    public String getItemsSummary() { ... } // 封装详细信息
}

2. ​合成复用原则(Composite Reuse Principle, CRP)​

定义:优先使用对象组合而非继承来实现代码复用。
示例:用组合代替继承实现"汽车"功能

java

// 继承方式(难以扩展)
class SportsCar extends Car {
    private Turbocharger turbo;
}

// 组合方式(更灵活)
class Car {
    private Engine engine;
    private Turbocharger turbo;
    
    public Car(Engine engine) {
        this.engine = engine;
    }
    
    public void addTurbo(Turbocharger turbo) {
        this.turbo = turbo;
    }
}

三、设计模式与原则的关系

设计模式 实现的设计原则 典型场景
工厂模式 OCP, DIP 对象创建解耦
观察者模式 OCP, SRP 事件驱动架构
策略模式 OCP, SRP 动态算法切换
适配器模式 ISP, DIP 接口兼容
装饰器模式 OCP, SRP 功能动态扩展

四、最佳实践总结

  1. 优先使用接口:Java中接口支持多继承,适合定义多个独立的行为契约
  2. 合理使用抽象类:当需要共享部分实现时使用抽象类(Java 8+支持default方法)
  3. 避免过度设计:遵循YAGNI原则(You Ain't Gonna Need It)
  4. 代码可读性优先:保持方法单一职责(建议控制在5-10行内)
  5. 使用设计模式:但不应为了模式而模式化
  6. 文档化设计决策:通过注释说明关键设计选择

五、反模式警示

  1. 上帝类(God Class)​:一个类承担过多职责(如SystemUtils包含文件操作、网络请求、数据库操作等)
  2. 字符串拼接地狱String result = "Hello " + user.getName() + "!" → 改用StringBuilder
  3. 过度继承:形成长继承链(如Animal → Mammal → Dog → Labrador),建议使用组合代替
  4. 隐藏对象状态:不提供必要的getter/setter方法

java

// 错误示例:暴露内部数据结构
public class User {
    public Map<String, String> attributes; // 不安全!
}

// 正确示例:封装属性访问
public class User {
    private Map<String, String> attributes;
    
    public String getAttribute(String key) {
        return attributes.get(key);
    }
    
    public void setAttribute(String key, String value) {
        attributes.put(key, value);
    }
}

六、实际项目中的权衡

  1. 性能 vs 可维护性:在高性能场景可能需牺牲部分OOP特性
  2. 团队协作:复杂的设计模式可能增加团队理解成本
  3. 框架限制:某些框架(如Spring Boot)会预设设计模式
  4. 技术债务:短期快速交付可能需要简化设计

通过系统应用这些设计原则,可以构建出高内聚、低耦合、易扩展的软件系统。记住:原则是指导方针而非铁律,实际开发中需要根据具体场景灵活平衡。