设计模式七大原则详解(小白也能懂)

哈喽,大家好,欢迎回到小码农岛!关注公众号:小码农岛 可查看更多文章,所有文章都会优先发布在公众号上。

话不多说,我们回到正题。

开闭原则(OCP)

简单来说

能方便地加新功能,但别改老代码

就像玩乐高积木,你可以随时加新积木块(扩展),但不用拆已经搭好的部分(不改动原有代码)。

为啥这么重要?

  • 改老代码容易带出bug
  • 加新功能时不用重新测试老代码

代码

错误案例

java 复制代码
// 这个绘图工具每次加图形都得改代码
class GraphicEditor {
    public void drawShape(Shape s) {
        if (s.type == 1) drawRectangle();  // 加个三角形又得新增if判断
        else if (s.type == 2) drawCircle();
        // 以后每加新图形都要改这里
    }
}

问题:每次新增图形类型都得修改核心方法,迟早要出问题。

正确姿势

java 复制代码
// 先定个规矩:所有图形都得会自己画自己
abstract class Shape {
    public abstract void draw();  // 强制子类自己实现
}

// 圆形自己管怎么画
class Circle extends Shape {
    @Override
    public void draw() {  
        System.out.println("画个圆");
    }
}

// 工具类根本不用改
class GraphicEditor {
    public void drawShape(Shape shape) {
        shape.draw();  // 甭管什么图形,调就完事了
    }
}

好处 :要加三角形?直接新建个Triangle类就行,其他代码不用动。

单一职责原则(SRP)

说人话

一个类只干一件事

就像餐厅里炒菜归厨师,端盘子归服务员,各司其职。

为什么要拆开?

  • 改一个功能不影响其他
  • 更容易重复使用

代码

错误案例

java 复制代码
class UserService {
    // 又管数据库又管业务逻辑
    public UserDTO getUser(String name) {
        Connection conn = DriverManager.getConnection(...); // 直接连数据库
        // 写SQL...
        // 转DTO...
    }
}

问题:哪天要换数据库,整个业务逻辑都得跟着改。

正确拆分

java 复制代码
// 专门管数据库的
class UserDao {
    public User queryFromDB(String name) { 
        // 专业处理数据库操作
    }
}

// 专门管业务的
class UserService {
    private UserDao dao;  // 用接口不直接依赖
    
    public UserDTO getUser(String name) {
        User user = dao.queryFromDB(name);
        // 专心处理业务转换
        return new UserDTO(user);
    }
}

好处:换数据库只要改Dao层,业务代码稳如老狗。

里氏替换原则(LSP)

核心要点

子类要能当父类用

就像手机充电口,type-C可以替换USB,但功能不受影响。

关键注意

  • 不能缩小方法权限(父类public的方法子类不能变private)
  • 不能改变核心功能(父类算减法,子类不能改成加法)

经典案例

java 复制代码
// 基础版:鸟会飞
class Bird {
    public void fly() { 
        System.out.println("扑棱翅膀飞"); 
    }
}

// 反面教材:企鹅继承鸟
class Penguin extends Bird {  // 要闯祸!
    @Override
    public void fly() {
        throw new UnsupportedOperationException(); // 企鹅不会飞啊
    }
}

// 正确打开方式
interface Flyable { void fly(); }
interface Swimmable { void swim(); }

class Sparrow implements Flyable { ... }  // 麻雀会飞
class Penguin implements Swimmable { ... } // 企鹅会游泳

启示:别让子类做不可能完成的任务,用接口更灵活。

依赖倒置原则(DIP)

说人话

对着接口编程,别死磕具体实现

就像用插座充电,管你是手机还是台灯,符合插头标准就能用。

实现套路

  1. 业务层定接口标准
  2. 具体实现类按标准来
  3. 用的时候把实现类"注射"进去

代码示例

java 复制代码
// 先定义发消息的标准
interface MessageSender {
    void send(String message);
}

// 短信发送实现
class SmsSender implements MessageSender {
    public void send(String msg) {
        System.out.println("发短信:" + msg);
    }
}

// 邮件发送实现
class EmailSender implements MessageSender {
    public void send(String msg) {
        System.out.println("发邮件:" + msg);
    }
}

// 业务类只管标准不管实现
class NotificationService {
    private MessageSender sender;
    
    // 要什么发送方式就从外面传
    public NotificationService(MessageSender sender) {
        this.sender = sender;  
    }
    
    public void alert(String message) {
        sender.send(message);  // 按标准调用
    }
}

优势:要换发送方式?换个实现类就行,业务代码纹丝不动。

接口隔离原则(ISP)

核心思想

宁要多个小接口,不要一个大而全

就像工具箱,螺丝刀归螺丝刀,锤子归锤子,比瑞士军刀好用。

什么时候拆?

  • 接口里有些方法总用不上
  • 不同客户端只用部分方法

代码示例

臃肿接口

java 复制代码
interface Animal {
    void eat();
    void fly();  // 企鹅:我招谁惹谁了?
    void swim(); // 麻雀:关我啥事?
}

问题:实现类被迫写没用的方法。

优化方案

java 复制代码
interface Eatable { void eat(); }
interface Flyable { void fly(); }
interface Swimmable { void swim(); }

class Sparrow implements Eatable, Flyable {...}
class Penguin implements Eatable, Swimmable {...}

好处:需要什么功能就实现什么接口,清爽!

合成复用原则(CARP)

说人话

多用组合,少用继承

就像组装电脑,买显卡插主板(组合)比改造主板电路(继承)靠谱多了。

组合 vs 继承

继承 组合
关系 是父类的一种(汽车是交通工具) 包含组件(汽车有发动机)
灵活度 改父类影响所有子类 随时换零件
耦合度 高(绑死父类) 低(接口对接)

代码示例

java 复制代码
// 用组合实现功能
class Car {
    private Engine engine;  // 发动机作为零件
    
    public Car(Engine engine) {
        this.engine = engine;  // 想换引擎?传进来就行
    }
    
    public void start() {
        engine.ignite();  // 调用引擎接口
    }
}

interface Engine {
    void ignite();
}

// 各种引擎实现
class GasEngine implements Engine {...}
class ElectricEngine implements Engine {...}

优势:油车改电车?换个电动引擎实现类就行。

迪米特法则(LOD)

简单理解

知道的越少越好

就像租房找中介,不用直接联系房东、物业、装修队。

注意事项

  1. 只和直接朋友打交道(自己的属性、方法参数、返回值)
  2. 不暴露复杂内部结构(返回List而不是ArrayList)
  3. 避免长链式调用(别写a.getB().getC().doSomething())

代码示例

java 复制代码
// 违反法则的写法
class Teacher {
    public void command(Monitor monitor) {
        List<Student> students = monitor.getStudents();  // 拿到学生名单
        for(Student s : students) {  // 直接指挥每个学生
            s.clean();
        }
    }
}

// 符合法则的写法
class Teacher {
    public void command(Monitor monitor) {
        monitor.cleanClassroom();  // 让班长负责具体安排
    }
}

class Monitor {
    public void cleanClassroom() {
        // 内部怎么分配我不管
    }
}

好处:老师不用知道学生怎么打扫,只管下命令就行。

总结对比表

原则名称 核心要点 常用场景 容易踩的坑
开闭原则 加功能不改老代码 功能扩展、插件开发 过度设计搞出复杂抽象
单一职责 一个类只干一件事 代码分层、工具类拆分 拆太细导致类太多
里氏替换 子类能当父类用 接口实现、算法替换 子类乱改父类功能
依赖倒置 面向接口编程 模块解耦、依赖注入 直接new具体实现类
接口隔离 按需拆分小接口 权限管理、功能拆分 设计万能接口
合成复用 多用组合少继承 组件开发、功能扩展 继承层级太深
迪米特法则 减少不必要的交互 模块通信、系统封装 链式调用暴露内部结构

这些原则就像做饭的调料,用好了能提升代码质量,但别死板硬套。新手建议多练习代码重构,慢慢体会什么时候该用什么原则。记住:适合的才是最好的!

最后想说

如果还在为面试八股文头疼?关注公众号 小码农岛 ,后台发送 66 免费领取《面试高频题宝典》,涵盖Java/MySQL/Redis/分布式等热门考点,助你轻松斩获Offer!

OK,今天的分享就到这里。欢迎「点赞+关注」支持!你对此有什么看法?欢迎在评论区分享您的观点。最后非常感谢大家的支持!你们的支持是我写作路上最大的动力。我们下期再见!

相关推荐
士别三日&&当刮目相看11 分钟前
JAVA学习*接口
java·学习
spencer_tseng15 分钟前
BlockChain.java
java·blockchain
可乐加.糖40 分钟前
腾讯云创建DeepSeek AI应用及使用教程
java·语言模型·腾讯云·deepseek
Lian_Aseubel1 小时前
Springboot整合Netty简单实现1对1聊天(vx小程序服务端)
java·spring boot·后端
m0_748254881 小时前
SpringBoot整合MQTT最详细版(亲测有效)
java·spring boot·后端
Monika Zhang2 小时前
Maven 简介及其核心概念
java·maven
时光呢2 小时前
JAVA泛型的作用
java·windows·python
树懒_Zz2 小时前
Apache Tomcat CVE-2025-24813 安全漏洞
java·tomcat·apache
天天进步20152 小时前
如何用Kafka实现优先级队列
java·kafka
九思x3 小时前
Spring(以 Spring Boot 为核心)与 JDK、Maven、MyBatis-Plus、Tomcat 的版本对应关系及关键注意事项
java