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

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

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

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


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

相关文章
|
1月前
|
SQL 数据采集 人工智能
评估工程正成为下一轮 Agent 演进的重点
面向 RL 和在数据层(SQL 或 SPL 环境)中直接调用大模型的自动化评估实践。
947 221
|
29天前
|
数据采集 缓存 数据可视化
Android 无侵入式数据采集:从手动埋点到字节码插桩的演进之路
本文深入探讨Android无侵入式埋点技术,通过AOP与字节码插桩(如ASM)实现数据采集自动化,彻底解耦业务代码与埋点逻辑。涵盖页面浏览、点击事件自动追踪及注解驱动的半自动化方案,提升数据质量与研发效率,助力团队迈向高效、稳定的智能化埋点体系。(238字)
446 158
|
28天前
|
人工智能 安全 Java
Spring AI 核心架构解析:构建企业级 AI 应用的 Java 新范式
Spring AI 为 Java 开发者提供企业级 AI 应用新范式,通过分层架构、统一抽象(如 ChatClient、PromptTemplate)与 Spring 生态深度集成,支持 RAG、函数调用、流式响应等核心功能,实现安全、可观测、可维护的智能系统构建。
478 8
|
1月前
|
SQL Java 数据库连接
MyBatis 与 Spring Data JPA 核心对比:选型指南与最佳实践
本文深入对比Java持久层两大框架MyBatis与Spring Data JPA,从核心理念、SQL控制力、开发效率、性能优化到适用场景,全面解析两者差异。MyBatis灵活可控,适合复杂SQL与高性能需求;JPA面向对象,提升开发效率,适用于标准CRUD系统。提供选型建议与混合使用策略,助力技术决策。
390 158
|
24天前
|
人工智能 前端开发 数据库
2025年最适合初学者的AI编程工具Top3:零基础也能写出专业级代码_ai代码生成器推荐
李响团队推荐2025年三款适合零基础的AI编程工具:Lynx AI、CodePal、DevMate。无需代码基础,说句话就能生成程序,帮你快速入门、边用边学,轻松迈入编程世界。
1054 154
|
24天前
|
消息中间件 安全 NoSQL
阿里云通过中国信通院首批安全可信中间件评估
近日,由中国信通院主办的 2025(第五届)数字化转型发展大会在京举行。会上,“阿里云应用服务器软件 AliEE”、“消息队列软件 RocketMQ”、“云数据库 Tair”三款产品成功通过中国信通院“安全可信中间件”系列评估,成为首批获此认证的中间件产品。此次评估覆盖安全可信要求、功能完备性、安全防护能力、性能表现、可靠性与可维护性等核心指标,标志着阿里云中间件产品在多架构适配与安全能力上达到行业领先水平。
384 204
|
27天前
|
机器学习/深度学习 人工智能 自然语言处理
智能自动化全方位解读:定义、技术、市场、应用,一文解答所有
智能自动化融合AI与RPA技术,重塑企业效率。它可处理非结构化数据、跨系统协同、自主决策,广泛应用于财务、人力、制造等领域,助力降本增效、合规创新。2025年全球市场规模将达4200亿美元,中国超9500亿元,成为最大单一市场。实在Agent等标杆产品实现“一句话完成工作”,5000+企业已落地应用,部署快、回报高、无需改造系统,正推动人机协同的数字革命。
|
2月前
|
人工智能 文字识别 并行计算
为什么别人用 DevPod 秒启 DeepSeek-OCR,你还在装环境?
DevPod 60秒极速启动,一键运行DeepSeek OCR大模型。告别环境配置难题,云端开箱即用,支持GPU加速、VSCode/Jupyter交互开发,重塑AI原生高效工作流。
681 35

热门文章

最新文章