文章目录
基层程序员世界中,我们经常会遇到形形色色的产品需求,在快速的迭代中,我们设计的代码会变得越来越臃肿。之所以如此,来源于我们没有抽出时间来做更好的抽象设计,仅仅是基于SpringMVC的
Controller、Service、Repository三层分层设计。我们把诸如更多的业务逻辑代码通过一个个方法成员方法不断编织在Service层,呈现给后续的其他研发人员,就是各种private修饰的方法,一个Service类代码行数随着日积月累不断堆积,我相信大家心有体会。试想一下,我们未尝不想更好的设计,以提高程序的可维护性,但是对于我们应该怎么可以做到更好的抽象设计,来达到避免这种现象产生呢,本文就是一篇指导案例,带给你心灵的共鸣。
一、需求背景
有个产品需求,需要做一个统计查询,从交互设计上就是两个选项卡,也代表两种不同统计口径。
- 第一种口径就是:广告曝光日期+直播间ID,作为查询条件。
- 第二种口径就是:广告曝光日期+直播间ID+职位编号,作为查询条件。
本质上这两种查询统计口径,其实业务处理逻辑有相似之处,但亦有差异之处,我们怎么设计才能更好的复用共性代码呢?同时也能处理各自场景的差异化代码呢?其实这里我们汇能想到,学过设计模式的程序员都能想到,使用“模板方法模式”,就是可以针对处理这种需求呃。同时后续再有其他场景的统计也可以进而复用这部分抽象设计呢,鉴于此,我通过思考这种问题,有了本篇文章的产生。
二、详细设计
UML设计

如上图,在这其中,有四个关键的类,StatCaliberEnum、StatHandleDispatcher、StatContext、AbstractStatHandle,这四个类是整个模块的关键所在。
包设计
整个统计模块包划分,位于business层的dashboard包下,后续新增场景只需要在handler子包增加子包(类似videoad,这是一个视频广告的统计查询)。如果某个场景增加统计口径,只需要在对应场景子包中新增子类Handler来继承基类(AbstractHandler)即可。
我们可以看出一个子包,只需要创建一个上下文类(譬如VideoAdStatContext)来继承基类StatContext,同时新增Hander来负责具体的业务处理。
每个子类做到职责单一,符合开闭原则,这就是整个模块的设计巧妙之处。

三、程序设计
1、StatCaliberEnum
所有统计场景模块的统计口径枚举。
它起到对具体场景的细分传统模式下,我们可能都会使用Integer或者String来表示,方法中来通过if else 程序条件来控制路由。但枚举的好处在于,我们把这种分支通过枚举成员来统一维护,通过查看枚举成员我们就可以知道当前场景的分类。后续增加场景,也只需要在枚举中增加成员即可。
/**
* 数据看板统计口径
*
* @author : 石冬冬-Sieg Heil
* @since 2022/11/30 9:39 AM
*/
@Getter
public enum StatCaliberEnum {
/**
* (视频广告)按天的明细数据
*/
VIDEO_AD_BY_DAILY(1, "按天的明细数据"),
/**
* (视频广告)按岗位的明细数据
*/
VIDEO_AD_BY_JOB_NUMBER(2, "按岗位的明细数据"),
;
private final int value;
private final String name;
StatCaliberEnum(Integer value, String name) {
this.value = value;
this.name = name;
}
}
2、VideoAdStatCaliberEnum
定义一个枚举,通过这个枚举对外暴露内部支持的业务场景,这是具体某个场景的细分。
它起到对具体场景的细分传统模式下,我们可能都会使用Integer或者String来表示,方法中来通过if else 程序条件来控制路由。但枚举的好处在于,我们把这种分支通过枚举成员来统一维护,通过查看枚举成员我们就可以知道当前场景的分类。后续增加场景,也只需要在枚举中增加成员即可。
/**
* 视频广告数据看板统计口径
*
* @author : 石冬冬-Sieg Heil
* @since 2022/11/30 9:39 AM
*/
@Getter
@ThriftStruct
public enum VideoAdStatCaliberEnum {
/**
* 未知
*/
UNKNOWN(-1, "未知"),
/**
* 按天的明细数据
*/
BY_DAILY(1, "按天的明细数据"),
/**
* 按岗位的明细数据
*/
BY_JOB_NUMBER(2, "按岗位的明细数据"),
;
@ThriftField(1)
private final int value;
private final String name;
VideoAdStatCaliberEnum(Integer value, String name) {
this.value = value;
this.name = name;
}
public static VideoAdStatCaliberEnum valueOf(Integer value) {
for (VideoAdStatCaliberEnum each : VideoAdStatCaliberEnum.values()) {
if (each.getValue() == value) {
return each;
}
}
return VideoAdStatCaliberEnum.UNKNOWN

本文通过一个产品需求案例,展示了如何运用模板方法设计模式来优化统计查询的代码结构,降低耦合度,提高可维护性。设计中包括StatCaliberEnum枚举用于区分统计口径,StatHandleDispatcher作为调度器分发不同的处理逻辑,以及通过AbstractStatHandler和具体场景的Handler子类实现业务逻辑。这样的设计使得新增或修改统计场景时,只需扩展相应Handler,符合开闭原则。
最低0.47元/天 解锁文章
6003

被折叠的 条评论
为什么被折叠?



