六大原则

面向对象设计的六大原则

面向对象设计(OOD)的六大原则是软件设计的基石,旨在提高代码的可维护性、可扩展性和复用性。这些原则相互关联,共同指导开发者构建灵活、健壮的系统。

开闭原则(Open-Closed Principle, OCP)

核心思想

对扩展开放,对修改关闭
即软件实体(类、模块、接口等)应允许通过扩展新增功能,而无需修改原有代码。

实现方式

  • 通过抽象基类接口定义稳定的核心逻辑,具体实现延迟到子类。
  • 新增功能时,只需添加新的子类或实现类,而非修改现有代码。

示例

// 抽象图形接口(稳定)
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)

核心思想

子类可以替换父类出现的任何地方,且替换后不会改变原有程序的正确性
即子类必须完全遵守父类的行为契约,不能破坏父类的功能逻辑。

违反场景

  • 子类重写父类方法时,改变了方法的预期行为(如返回值范围、异常抛出)。
  • 子类新增父类未声明的约束(如参数校验更严格)。

示例

// 父类:矩形
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)

核心思想

依赖于抽象,而非具体实现
即高层模块不应依赖低层模块,两者都应依赖抽象;抽象不应依赖细节,细节应依赖抽象。

实现方式

  • 通过接口抽象类定义交互契约,高层模块调用抽象接口。
  • 低层模块实现抽象接口,使高层模块与具体实现解耦。

示例

// 抽象接口(高层与低层共同依赖)
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)

核心思想

使用多个专用接口,而非一个通用接口
即接口应最小化,每个接口只包含客户端所需的方法,避免客户端依赖不需要的方法。

违反场景

  • 一个接口包含过多方法,导致实现类被迫实现无关方法。
  • 客户端依赖于其不使用的方法,增加耦合度。

示例

// 错误:通用接口包含过多方法
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 { // 机器人无需eat和sleep
    @Override 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);
    }
}

优势

  • 提高类的内聚性,使代码更简洁、易懂。
  • 降低修改风险,一个职责的变化不会影响其他职责。

总结

六大原则的核心目标是降低耦合、提高内聚,构建可扩展、可维护的系统:

  • 开闭原则:扩展优先于修改;
  • 里氏代换:子类替换不影响正确性;
  • 依赖倒转:面向抽象编程;
  • 接口隔离:接口最小化;
  • 迪米特原则:减少交互,弱耦合;
  • 单一职责:职责明确,降低复杂度