设计模式六大原则 — 列举反例详解各个原则的核心思想和意义

简介: 设计模式六大原则是面向对象设计的基石,涵盖单一职责、开闭原则、里氏替换、接口隔离、依赖倒置和迪米特法则,旨在提升代码可维护性、扩展性与可读性,实现高内聚、低耦合的软件架构。

@TOC

概述

设计模式的六大原则是面向对象设计的基石,遵循这些原则可以提升代码的可维护性、可扩展性和可读性。下面对六大原则进行详细解析。

一、单一职责原则 (Single Responsibility Principle, SRP)

1. 定义

一个类应该只有一个引起变化的原因,即:一个类只负责一项职责

2. 核心思想

  • 高内聚:类的功能要集中
  • 低耦合:类之间的依赖要少

3. 代码示例

违反 SRP 的例子

// 违反单一职责原则
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 的改进

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 的例子

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 的改进

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 的例子

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 的改进

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 的例子

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 的改进

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 的例子

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 的改进

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. 代码示例

违反迪米特法则的例子

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());
    }
}

问题:客户端需了解 CompanyEmployeeDepartment 的链式结构。

遵循迪米特法则的改进

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());
    }
}

更完整的例子:电脑启动

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) 最少知识原则 降低耦合、封装细节

八、综合应用示例

// 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); // 客户端无需知道细节
    }
}

总结
这六大原则是构建高质量、可维护、可扩展软件系统的基础。在实际开发中,应灵活运用这些原则,避免过度设计,做到“恰到好处的抽象”。


提示:原则是指导,不是教条。结合业务场景合理使用才是关键。

相关文章
|
9天前
|
人工智能 数据可视化 Java
Spring AI Alibaba、Dify、LangGraph 与 LangChain 综合对比分析报告
本报告对比Spring AI Alibaba、Dify、LangGraph与LangChain四大AI开发框架,涵盖架构、性能、生态及适用场景。数据截至2025年10月,基于公开资料分析,实际发展可能随技术演进调整。
761 150
|
2天前
|
数据可视化 Java Nacos
OpenFeign + Sentinel 实现微服务熔断限流实战
本文介绍如何在Spring Cloud微服务架构中,结合OpenFeign与阿里巴巴开源组件Sentinel,实现服务调用的熔断、降级与限流。通过实战步骤搭建user-service与order-service,集成Nacos注册中心与Sentinel Dashboard,演示服务异常熔断、QPS限流控制,并支持自定义限流响应。借助Fallback降级机制与可视化规则配置,提升系统稳定性与高可用性,助力构建健壮的分布式应用。
215 155
|
2天前
|
负载均衡 Java Maven
OpenFeign:让微服务调用像本地方法一样简单
OpenFeign是Spring Cloud的声明式HTTP客户端,通过接口+注解方式简化微服务间调用。无需手动编写请求代码,像调用本地方法一样发起远程调用,支持负载均衡、熔断降级、请求拦截等特性,极大提升开发效率与系统可靠性。
246 156
|
18天前
|
人工智能 运维 Java
Spring AI Alibaba Admin 开源!以数据为中心的 Agent 开发平台
Spring AI Alibaba Admin 正式发布!一站式实现 Prompt 管理、动态热更新、评测集构建、自动化评估与全链路可观测,助力企业高效构建可信赖的 AI Agent 应用。开源共建,现已上线!
1607 40
|
15天前
|
文字识别 测试技术 开发者
Qwen3-VL新成员 2B、32B来啦!更适合开发者体质
Qwen3-VL家族重磅推出2B与32B双版本,轻量高效与超强推理兼备,一模型通吃多模态与纯文本任务!
1029 11