架构Lyon头像
关注

软件工程SOLID 五大设计原则

缩写全称中文核心一句话
SSingle Responsibility Principle单一职责原则一个类只负责一件事
OOpen/Closed Principle开闭原则对扩展开放,对修改关闭
LLiskov Substitution Principle里氏替换原则子类可以完全替换父类,程序不出错
IInterface Segregation Principle接口隔离原则不要强迫类实现它不需要的接口
DDependency Inversion Principle依赖倒置原则高层、低层模块都依赖抽象,不依赖具体实现

S 单一职责 SRP

定义:一个类只有一个变更理由
业务场景:订单实体不要同时做业务计算、持久化、发消息。

❌ 反面(违反SRP,新手常写)

// 坏例子:一个类干三件事
public class Order {
    private Long id;
    private BigDecimal amount;
    // 1. 业务计算
    public BigDecimal calcDiscount() { ... }
    // 2. 数据库保存
    public void saveToDb() { ... }
    // 3. 发送MQ消息
    public void sendCreateEvent() { ... }
}

问题:改优惠规则、改数据库字段、改MQ消息体,都要修改Order类,极易引入bug。

✅ 正确拆分

// 1. 领域实体:仅保存订单状态和基础属性(只负责订单自身数据)
public class Order {
    private Long id;
    private BigDecimal amount;
    public BigDecimal calcDiscount(DiscountRule rule) { ... }
}
// 2. 仓储层:只负责数据库读写
public class OrderRepository {
    public void save(Order order) { ... }
}
// 3. 事件发送:只负责消息投递
public class OrderEventPublisher {
    public void publishOrderCreated(Order order) { ... }
}

变更隔离:改MQ只碰OrderEventPublisher,改SQL只碰OrderRepository。


O 开闭原则 OCP

定义:对扩展开放,对修改关闭;新增优惠类型,不改动原有订单计算代码

业务:电商优惠,满减、优惠券、会员折扣;后续还要新增限时秒杀折扣。

❌ 反面:if-else 硬编码

public class DiscountService {
    public BigDecimal calculate(Order order, String discountType){
        if("FULL_REDUCE".equals(discountType)){
            return fullReduce(order);
        }else if("COUPON".equals(discountType)){
            return coupon(order);
        }
        // 新增秒杀折扣 → 必须修改这个方法,违反OCP
        return BigDecimal.ZERO;
    }
}

✅ 正向:抽象优惠策略

// 抽象优惠接口
public interface DiscountStrategy {
    BigDecimal compute(Order order);
}
// 已有实现:满减
public class FullReduceStrategy implements DiscountStrategy{}
// 已有实现:优惠券
public class CouponStrategy implements DiscountStrategy{}

// 计算服务,不需要修改,新增优惠只新增Strategy类
public class DiscountService {
    private DiscountStrategy strategy;
    public DiscountService(DiscountStrategy strategy){
        this.strategy = strategy;
    }
    public BigDecimal getDiscount(Order order){
        return strategy.compute(order);
    }
}

新增秒杀折扣:新建SeckillDiscountStrategy,原有DiscountService完全不动。


L 里氏替换 LSP

定义:子类替换父类,业务行为不变,不破坏原有逻辑契约

业务场景:支付渠道。父类定义支付接口契约。

❌ 反面(经典业务坑,违反LSP)

public abstract class PaymentChannel {
    // 契约:正常情况支付成功返回true;余额不足抛业务异常
    public abstract boolean pay(Long orderId, BigDecimal money);
}
// 子类:信用支付(先赊账,不用扣钱)
public class CreditPayment extends PaymentChannel{
    @Override
    public boolean pay(Long orderId, BigDecimal money) {
        // 破坏契约:无论金额多大都返回true,不做额度校验
        // 原有上层代码以为已经扣款,实际只是记账,业务逻辑直接出错
        return true;
    }
}

问题:上层订单结算代码,把CreditPayment当作PaymentChannel传入,行为不符合父类约定,业务产生资金漏洞。

✅ 正确:子类严格遵守父类行为契约

public class CreditPayment extends PaymentChannel{
    @Override
    public boolean pay(Long orderId, BigDecimal money) {
        // 遵守契约:额度不足抛出同样类型业务异常,上层代码可统一处理
        if(creditLimit.compareTo(money) < 0){
            throw new BizException("信用额度不足");
        }
        // 记账
        return true;
    }
}

替换后,上层订单结算逻辑完全不用改动,行为预期一致。

LSP核心:不光方法签名能继承,业务行为契约也要一致。


I 接口隔离 ISP

定义:客户端不依赖不需要的接口,胖接口拆成多个小接口

业务:订单的各种操作:创建订单、取消订单、退款、导出订单报表。
后台有两种客户端:普通下单服务、财务报表系统。

❌ 反面:大胖接口

// 胖接口,所有实现类被迫实现全部方法
public interface OrderOperator {
    void createOrder(Order order);
    void cancelOrder(Long orderId);
    void refund(Long orderId);
    void exportReport(); // 报表导出
}
// 下单服务实现这个接口,根本不需要exportReport,只能写空实现
public class OrderBizService implements OrderOperator{
    @Override
    public void exportReport() {
        // 空方法,污染代码,容易被误调用
    }
}

✅ 拆分细粒度接口

public interface OrderCrud {
    void createOrder(Order order);
    void cancelOrder(Long orderId);
    void refund(Long orderId);
}
public interface OrderReport {
    void exportReport();
}
// 业务服务只实现订单操作接口,不用管报表
public class OrderBizService implements OrderCrud{}
// 财务报表类单独实现报表接口
public class OrderReportService implements OrderReport{}

业务类只依赖自己需要的接口,不会被迫实现无关能力。


D 依赖倒置 DIP

高层业务不依赖低层具体实现,两者依赖抽象

业务:订单保存,底层可以切换MySQL / TiDB。

❌ 反面:高层直接new底层实现,硬耦合

// 高层业务服务,直接依赖MySQL具体实现
public class OrderService {
    // 写死MySQL,换TiDB要修改OrderService代码
    private MysqlOrderDao orderDao = new MysqlOrderDao();
    public void createOrder(Order order){
        orderDao.insert(order);
    }
}

✅ 正向:依赖抽象仓储接口

// 抽象(高层、底层共同依赖抽象)
public interface OrderRepository {
    void insert(Order order);
}
// 底层实现
public class MysqlOrderRepository implements OrderRepository{}
public class TidbOrderRepository implements OrderRepository{}

// 高层业务,只依赖抽象,不关心数据库类型(Spring DI注入)
public class OrderService {
    private final OrderRepository repository;
    // 注入抽象,而不是具体Mysql实现
    public OrderService(OrderRepository repository){
        this.repository = repository;
    }
    public void createOrder(Order order){
        repository.insert(order);
    }
}

切换数据库,只更换注入的Repository实现,OrderService一行不动。

Spring IOC核心思想就是落地DIP。


生产落地小总结

  1. SRP:DDD领域实体 + 仓储分离就是SRP实践;
  2. OCP:策略模式做优惠、渠道、规则,是最常用落地;
  3. LSP:支付、库存子类实现最容易踩坑,重点关注异常、返回值契约;
  4. ISP:不要在一个接口堆一堆能力,按调用方去拆分接口;
  5. DIP:业务层依赖Repository接口,不直接依赖Mybatis/Mongo实现类。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/Maolei_NyaRu_/article/details/166246124

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--