让代码变美的第一天 - 观察者模式

文章目录

丑陋的模样

当我们开发一个功能,代码可能如下:

java 复制代码
private void test() {
    func1();
    
    func2();
    
    func3();
}
  • 目前看起来还是美美哒,结构清晰,层次分明
    新需求来的时候,慢慢的变成了下面这样:
java 复制代码
private void test() {
    func1();
    try {
        func2();
    } catch (Exception e) {

    }
    func3();

    func4();

    // func5
    if (1 == 1) {
        for (int i = 0; i < 5; i++) {
            // todo1
            // todo2
        }
    }
}
  • 多人开发的话,情况更严重。当项目大了里面的逻辑很少有人能全部讲清楚,老代码没人敢动(劝你也不要动,一年到头辛辛苦苦,背个故障从头来过)
  • 代码会越来越乱,在一个方法里面堆砌的代码会越来越多,如果有洁癖一点的可能还会把新代码封装成func4,如果偷懒可能就变成了func5()下面的代码了,屎山代码可能就是这样出来的吧

变美步骤

第一步 - 基本预期

思考:这个功能,这个方法改动到底是不是很频繁。有个预期就行,如果没有预期,那就看实际改动次数。

  • 如果不频繁,建议就这样吧,只要结构清晰,方便review代码就行。不鼓励过度设计,毕竟开发时间都是有限的。
  • 如果很频繁,就看第二步

第二步 - 核心逻辑梳理

还是上面那个代码,需要思考到底哪部分是核心,假设func1、func2、func3是库存校验、下单、修改库存。func4、func5是销量统计、发货

那么很容易判断出,func123才是核心逻辑,是下单的必要步骤。func45就是下单完之后的一些后续处理,甚至可能都不需要同步处理,比如发货,和下单并没有强绑定。

很容易就判断出来,这可能是一个发布订阅的逻辑,可以用观察者模式来重构,学了这么多年的设计模式,这不就能用上了么。

第三步 - 重构

  • 如果未来计划很明确,项目越做越大,肯定要拆分成多个微服务,组成一个分布式服务,并且已经有消息中间件了(Kafka、RocketMQ等),那么就直接上吧,用发布订阅来异步、解耦、削峰。
  • 如果还没有那么明确的目标,也不想引入大型中间件,那么重构一下代码还是很不错的。

重构1 - 消息定义

只讨论使用spring项目

java 复制代码
public class OrderEvent {
    private long id;
    private String desc;
}
  • 需要向下传递的参数信息

重构2 - 消息订阅

java 复制代码
@Service
public class OrderEventListenerService {
    @EventListener
    public void handleForOrderSaveEvent(OrderEvent event) {
        log.info("收到接单消息:{}", event);
    }
}
  • 这里订阅的就是func4/5

重构3 - 消息发布

java 复制代码
@Service
public class OrderEventPublishService {
    @Autowired
    private ApplicationEventPublisher eventPublisher;

    public void order() {
        OrderEvent event = new OrderEvent(1, "接单");
        log.info("发送接单消息:{}", event);
        eventPublisher.publishEvent(event);
        log.info("消息发送完毕:{}", event);
    }
}
  • 这里就是func1/2/3
    三步就搞定了重构,并且下次修改,只需要新增一个订阅方法。

高级用法

按顺序订阅

func4、func5是需要有先后顺序的,那么只需要加上@Order(num),按到num从小到大的顺序先后处理

java 复制代码
@Service
public class OrderEventListenerService {
    @Order(2)
    @EventListener
    public void handleForOrderSaveEvent1(OrderEvent event) {
        log.info("2同步收到接单消息:{}", event);
    }

    @Order(1)
    @EventListener
    public void handleForOrderSaveEvent2(OrderEvent event) {
        log.info("1同步收到接单消息:{}", event);
    }
}

异步订阅

如果func5是发货,完全不依赖下单,订阅方就可以异步处理,使用@Async就行了。

java 复制代码
@Service
public class OrderEventListenerService {
    @Async
    @EventListener
    public void asyncHandleForOrderSaveEvent(OrderEvent event) {
        log.info("异步收到接单消息:{}", event);
    }
}

注意:使用的是默认线程池,最好改成自定义的线程池

多消息订阅

如果func5是发货,除了C端用户下单以外,B端用户也能下单,但是B端下单代码完全是另一份,那么就需要有B端的下单消息,func5统一处理发货流程就行了

java 复制代码
@Service
public class OrderEventListenerService {
    @Async
    @EventListener(classes = {OrderAEvent.class, OrderBEvent.class})
    public void asyncHandleForOrderSaveEvent(Object event) {
        log.info("异步收到接单消息:{}", event);
    }
}
相关推荐
float_六七3 小时前
IntelliJ IDEA双击Ctrl的妙用
java·ide·intellij-idea
能摆一天是一天5 小时前
JAVA stream().flatMap()
java·windows
颜如玉5 小时前
🤲🏻🤲🏻🤲🏻临时重定向一定要能重定向🤲🏻🤲🏻🤲🏻
java·http·源码
程序员的世界你不懂7 小时前
【Flask】测试平台开发,新增说明书编写和展示功能 第二十三篇
java·前端·数据库
星空寻流年7 小时前
设计模式第一章(建造者模式)
java·设计模式·建造者模式
gb42152877 小时前
java中将租户ID包装为JSQLParser的StringValue表达式对象,JSQLParser指的是?
java·开发语言·python
曾经的三心草8 小时前
Python2-工具安装使用-anaconda-jupyter-PyCharm-Matplotlib
android·java·服务器
Metaphor6928 小时前
Java 高效处理 Word 文档:查找并替换文本的全面指南
java·经验分享·word
ChinaRainbowSea8 小时前
7. LangChain4j + 记忆缓存详细说明
java·数据库·redis·后端·缓存·langchain·ai编程
stormsha8 小时前
飞算JavaAI炫技赛电商系统商品管理模块的架构设计与实现
java·架构·鸿蒙系统