Java示例:设计模式是如何在实战中“自然生长”出来

2025-12-12 0 654

您问到了最关键的点子上,也是所有学习者从理论到实践最难跨越的一步。理论之所以感觉“一套套的”,是因为它被系统地、干净地总结了出来。而现实是混乱、复杂且充满约束的。

下面,我抛弃纯理论,用一个 实战推演 的思路,带你看看设计模式是如何在实战中“自然生长”出来的。

核心理念:模式不是起点,而是重构的结果

不要带着“我要用模式”的心态去写代码,而要带着 “我要解决问题,让代码更干净” 的心态。模式是你到达终点后,别人给你起的名字。


实战推演:从一个简单需求开始

假设我们要开发一个电商系统的 OrderService,核心需求是创建订单。

第零步:最直接的实现

public class OrderService {
    public Order createOrder(CreateOrderRequest request) {
        // 1. 验证参数
        if (request.getItems().isEmpty()) {
            throw new ValidationException(\"订单项不能为空\");
        }
        // 2. 计算价格
        BigDecimal total = calculateTotal(request.getItems());
        // 3. 扣减库存(调用库存服务)
        inventoryClient.decreaseStock(request.getItems());
        // 4. 保存订单到数据库
        Order order = orderRepository.save(new Order(total, request.getItems()));
        // 5. 发送订单创建成功事件(比如发邮件、短信)
        eventBus.publish(new OrderCreatedEvent(order.getId()));
        return order;
    }
}

现状:代码能工作,逻辑清晰。此时,任何设计模式都是多余的。


第一步:需求变化 -> 引入“策略模式”的种子

新需求:现在要支持不同类型的订单:普通订单、团购订单、秒杀订单。它们的价格计算规则完全不同。

糟糕的改法:在 calculateTotal 方法里加 if-else

// 坏味道:冗长的if-else
private BigDecimal calculateTotal(OrderType type, List items) {
    if (type == OrderType.NORMAL) {
        // ... 普通计算逻辑
    } else if (type == OrderType.GROUP_BUY) {
        // ... 团购计算逻辑
    } else if (type == OrderType.FLASH_SALE) {
        // ... 秒杀计算逻辑
    }
    // ... 未来每加一种类型,就要修改这里
}

问题:违反了“开闭原则”(对扩展开放,对修改关闭)。每次加新类型,都要修改这个核心类,风险高。

重构(模式自然浮现)

  1. 识别变化点:价格计算逻辑是变化的。
  2. 抽取接口:定义一个 PricingStrategy 接口。
    public interface PricingStrategy {
        boolean supports(OrderType type);
        BigDecimal calculate(List items);
    }
    
  3. 封装实现:为每种订单类型创建一个策略类。
    @Component
    public class NormalPricingStrategy implements PricingStrategy {
        @Override public boolean supports(OrderType type) { return type == OrderType.NORMAL; }
        @Override public BigDecimal calculate(List items) { /* ... */ }
    }
    // ... 其他策略类
    
  4. 在Service中使用策略
    @Autowired
    private List strategies; // Spring会自动注入所有实现
    
    private BigDecimal calculateTotal(OrderType type, List items) {
        for (PricingStrategy strategy : strategies) {
            if (strategy.supports(type)) {
                return strategy.calculate(items);
            }
        }
        throw new UnsupportedOperationException(\"不支持的订单类型\");
    }
    

你看,我们并没有“使用策略模式”,而是通过“消除重复的if-else”和“隔离变化”,让策略模式的结构自然呈现了出来。 现在,增加新订单类型,我只需要新建一个 PricingStrategy 实现类,OrderService 完全不用动。


第二步:逻辑复杂 -> 引入“模板方法模式”的种子

新问题:创建订单的步骤越来越复杂,每种订单类型在核心步骤上一致(验证->算价->扣库存->保存->发事件),但某些步骤的实现细节不同(比如团购订单的验证逻辑更复杂)。

糟糕的改法:在 createOrder 方法里写满针对类型的 if-else。代码会变成一团乱麻。

重构(模式自然浮现)

  1. 识别不变与变流程骨架(模板)是不变的某些步骤的实现是可变的
  2. 定义模板:将固定流程提升到父类。
    public abstract class AbstractOrderCreator {
        // 这就是“模板方法”
        public final Order createOrder(CreateOrderRequest request) {
            // 固定流程
            validateSpecific(request);
            BigDecimal total = calculateTotal(request);
            decreaseStock(request);
            Order order = saveOrder(request, total);
            publishEvent(order);
            return order;
        }
        // 将变化的步骤声明为抽象方法,强迫子类实现
        protected abstract void validateSpecific(CreateOrderRequest request);
        protected abstract BigDecimal calculateTotal(CreateOrderRequest request);
        // ... 其他抽象或可重写的方法
    }
    
  3. 创建子类
    @Service
    public class GroupBuyOrderCreator extends AbstractOrderCreator {
        @Override
        protected void validateSpecific(CreateOrderRequest request) {
            // 团购订单特有的复杂验证逻辑
            if (!groupBuyService.isValid(request.getGroupId())) {
                throw new ValidationException(\"团购活动已失效\");
            }
            // ... 更多验证
        }
        // ... 实现其他抽象方法
    }
    

你看,我们为了解决“流程相同,部分步骤不同”的问题,自然地使用了模板方法模式。 现在,OrderService 的职责可以简化为路由,根据类型找到对应的 AbstractOrderCreator 来执行。


第三步:依赖混乱 -> 引入“依赖注入”与“门面模式”

新问题OrderService 现在依赖了太多东西:各种 CreatorPricingStrategy、仓库、客户端等。它变成了一个“上帝类”,难以测试和维护。

重构(模式自然浮现)

  1. 应用依赖注入:这不是一个具体的GoF模式,而是一个更基础的架构原则。我们通过构造函数清晰地声明依赖,而不是用 @Autowired 到处乱注。
    @Service
    public class OrderService {
        private final Map creatorMap;
        // 通过构造器注入,依赖关系一目了然
        public OrderService(List creators) {
            this.creatorMap = creators.stream()
                .collect(Collectors.toMap(creator -> creator.supportsOrderType(), Function.identity()));
        }
        public Order createOrder(CreateOrderRequest request) {
            AbstractOrderCreator creator = creatorMap.get(request.getType());
            return creator.createOrder(request);
        }
    }
    
  2. 考虑门面模式:如果外部客户端(如Controller)需要调用多个Service才能完成一个操作,我们可以引入一个 OrderFacade,为外部提供一个统一的、简洁的接口,隐藏内部 OrderServicePaymentServiceShippingService 的复杂交互。

给你的实践路线图

  1. 忘记模式,记住原则:首先牢记 SOLID 原则(尤其是单一职责SRP和开闭原则OCP)和 DRY(不要重复自己)。这些是产生模式的土壤。
  2. 从小处着手:不要试图设计一个完美的系统。先让代码跑起来。
  3. 培养嗅觉:学会识别“代码坏味道”。
    • 重复代码 -> 提取方法/类。
    • 过长的函数/类 -> 分解。
    • 庞大的条件判断 -> 考虑用多态(策略、状态模式)替代。
    • 类知道得太多 -> 考虑依赖注入、中介者模式。
  4. 痛苦驱动重构:当添加新功能或修改代码变得非常困难和痛苦时,就是重构的最佳时机。问问自己:“这里是什么变化让我如此痛苦?我能否将这部分抽离出来?”
  5. 重构,而非重写:通过一系列小步、安全的重构(有测试保障最好),将“坏代码”逐步改善为“好代码”。在这个过程中,你会发现设计模式的结构自然而然地出现了。

最终,你的目标不是“在代码中使用模式”,而是“写出干净的代码”。而干净的代码,往往会自然而然地体现出经典设计模式的结构。 这时,模式对你来说就不再是“一套套的理论”,而是你工具箱里顺手拈来的、有名字的工具了。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 编程相关 Java示例:设计模式是如何在实战中“自然生长”出来 https://www.zuozi.net/35929.html

常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务