作者:小傅哥
博客:https://bugstack.cn - 原创系列专题文章
沉淀、分享、成长,让自己和他人都能有所收获!😄
写好代码三个关键点
如果把写代码想象成家里的软装,你肯定会想到家里需要有一个非常不错格局最好是南北通透的,买回来的家具最好是品牌保证质量的,之后呢是大小合适,不能摆放完了看着别扭。那么把这一过程抽象成写代码就是需要三个核心的关键点;架构
(房间的格局)、命名
(品牌和质量)、注释
(尺寸大小说明书),只有这三个点都做好才能完成出一套赏心悦目的家。
平原走码🐎易放难收
上学期间你写了多少代码?上班一年你能写多少代码?回家自己学习写了多少代码?个人素养的技术栈地基都是一块一块砖码出来的,写的越广越深,根基就越牢固。当根基牢固了以后在再上层建设就变得迎刃而解了,也更容易建设了。往往最难的就是一层一层阶段的突破,突破就像破壳一样,也像夯实地基,短时间看不到成绩,也看不出高度。但以后谁能走的稳,就靠着默默的沉淀。
技术传承的重要性
可能是现在时间节奏太快,一个需求下来恨不得当天就上线(这个需求很简单,怎么实现我不管,明天上线!
),导致团队的人都很慌
、很急
、很累
、很崩溃
,最终反反复复的人员更替,项目在这个过程中也交接了N次,文档不全、代码混乱、错综复杂,谁在后面接手也都只能修修补补,就像烂尾楼。这个没有传承、没有沉淀的项目,很难跟随业务的发展。最终!根基不牢,一地鸡毛。
bugstack虫洞栈
,回复源码下载
获取(打开获取的链接,找到序号18)工程 | 描述 |
---|---|
itstack-demo-design-19-00 | 场景模拟工程;模拟营销活动操作服务(查询、审核) |
itstack-demo-design-19-01 | 使用一坨代码实现业务需求 |
itstack-demo-design-19-02 | 通过设计模式优化改造代码,产生对比性从而学习 |
状态模式描述的是一个行为下的多种状态变更,比如我们最常见的一个网站的页面,在你登录与不登录下展示的内容是略有差异的(不登录不能展示个人信息
),而这种登录
与不登录
就是我们通过改变状态,而让整个行为发生了变化。
至少80后、90后的小伙伴基本都用过这种磁带放音机(可能没有这个好看
),它的上面是一排按钮,当放入磁带后,通过上面的按钮就可以让放音机播放磁带上的内容(listen to 英语听力考试
),而且有些按钮是互斥的,当在某个状态下才可以按另外的按钮(这在设计模式里也是一个关键的点
)。
在本案例中我们模拟营销活动审核状态流转场景(一个活动的上线是多个层级审核上线的)
在上图中也可以看到我们的流程节点中包括了各个状态到下一个状态扭转的关联条件,比如;审核通过才能到活动中,而不能从编辑中直接到活动中,而这些状态的转变就是我们要完成的场景处理。
大部分程序员基本都开发过类似的业务场景,需要对活动或者一些配置需要审核后才能对外发布,而这个审核的过程往往会随着系统的重要程度而设立多级控制,来保证一个活动可以安全上线,避免造成资损。
当然有时候会用到一些审批流的过程配置,也是非常方便开发类似的流程的,也可以在配置中设定某个节点的审批人员。但这不是我们主要体现的点,在本案例中我们主要是模拟学习对一个活动的多个状态节点的审核控制。
itstack-demo-design-19-00 └── src └── main └── java └── org.itstack.demo.design ├── ActivityInfo.java ├── Status.java └── ActivityService.java
Status
)、活动对象(ActivityInfo
)、活动服务(ActivityService
),三个服务类。public class ActivityInfo { private String activityId; // 活动ID private String activityName; // 活动名称 private Enum<Status> status; // 活动状态 private Date beginTime; // 开始时间 private Date endTime; // 结束时间 // ...get/set }
public enum Status { // 1创建编辑、2待审核、3审核通过(任务扫描成活动中)、4审核拒绝(可以撤审到编辑状态)、5活动中、6活动关闭、7活动开启(任务扫描成活动中) Editing, Check, Pass, Refuse, Doing, Close, Open }
public class ActivityService { private static Map<String, Enum<Status>> statusMap = new ConcurrentHashMap<String, Enum<Status>>(); public static void init(String activityId, Enum<Status> status) { // 模拟查询活动信息 ActivityInfo activityInfo = new ActivityInfo(); activityInfo.setActivityId(activityId); activityInfo.setActivityName("早起学习打卡领奖活动"); activityInfo.setStatus(status); activityInfo.setBeginTime(new Date()); activityInfo.setEndTime(new Date()); statusMap.put(activityId, status); } /** * 查询活动信息 * * @param activityId 活动ID * @return 查询结果 */ public static ActivityInfo queryActivityInfo(String activityId) { // 模拟查询活动信息 ActivityInfo activityInfo = new ActivityInfo(); activityInfo.setActivityId(activityId); activityInfo.setActivityName("早起学习打卡领奖活动"); activityInfo.setStatus(statusMap.get(activityId)); activityInfo.setBeginTime(new Date()); activityInfo.setEndTime(new Date()); return activityInfo; } /** * 查询活动状态 * * @param activityId 活动ID * @return 查询结果 */ public static Enum<Status> queryActivityStatus(String activityId) { return statusMap.get(activityId); } /** * 执行状态变更 * * @param activityId 活动ID * @param beforeStatus 变更前状态 * @param afterStatus 变更后状态 b */ public static synchronized void execStatus(String activityId, Enum<Status> beforeStatus, Enum<Status> afterStatus) { if (!beforeStatus.equals(statusMap.get(activityId))) return; statusMap.put(activityId, afterStatus); } }
queryActivityInfo
、queryActivityStatus
、execStatus
。数据库
或者Redis
中获取。这里我们先使用最粗暴的方式来实现功能
对于这样各种状态的变更,最让我们直接想到的就是使用if
和else
进行判断处理。每一个状态可以流转到下一个什么状态,都可以使用嵌套的if
实现。
itstack-demo-design-19-01 └── src └── main └── java └── org.itstack.demo.design ├── ActivityExecStatusController.java └── Result.java
ActivityExecStatusController
、Result
,一个是处理流程状态,另外一个是返回的对象。public class ActivityExecStatusController { /** * 活动状态变更 * 1. 编辑中 -> 提审、关闭 * 2. 审核通过 -> 拒绝、关闭、活动中 * 3. 审核拒绝 -> 撤审、关闭 * 4. 活动中 -> 关闭 * 5. 活动关闭 -> 开启 * 6. 活动开启 -> 关闭 * * @param activityId 活动ID * @param beforeStatus 变更前状态 * @param afterStatus 变更后状态 * @return 返回结果 */ public Result execStatus(String activityId, Enum<Status> beforeStatus, Enum<Status> afterStatus) { // 1. 编辑中 -> 提审、关闭 if (Status.Editing.equals(beforeStatus)) { if (Status.Check.equals(afterStatus) || Status.Close.equals(afterStatus)) { ActivityService.execStatus(activityId, beforeStatus, afterStatus); return new Result("0000", "变更状态成功"); } else { return new Result("0001", "变更状态拒绝"); } } // 2. 审核通过 -> 拒绝、关闭、活动中 if (Status.Pass.equals(beforeStatus)) { if (Status.Refuse.equals(afterStatus) || Status.Doing.equals(afterStatus) || Status.Close.equals(afterStatus)) { ActivityService.execStatus(activityId, beforeStatus, afterStatus); return new Result("0000", "变更状态成功"); } else { return new Result("0001", "变更状态拒绝"); } } // 3. 审核拒绝 -> 撤审、关闭 if (Status.Refuse.equals(beforeStatus)) { if (Status.Editing.equals(afterStatus) || Status.Close.equals(afterStatus)) { ActivityService.execStatus(activityId, beforeStatus, afterStatus); return new Result("0000", "变更状态成功"); } else { return new Result("0001", "变更状态拒绝"); } } // 4. 活动中 -> 关闭 if (Status.Doing.equals(beforeStatus)) { if (Status.Close.equals(afterStatus)) { ActivityService.execStatus(activityId, beforeStatus, afterStatus); return new Result("0000", "变更状态成功"); } else { return new Result("0001", "变更状态拒绝"); } } // 5. 活动关闭 -> 开启 if (Status.Close.equals(beforeStatus)) { if (Status.Open.equals(afterStatus)) { ActivityService.execStatus(activityId, beforeStatus, afterStatus); return new Result("0000", "变更状态成功"); } else { return new Result("0001", "变更状态拒绝"); } } // 6. 活动开启 -> 关闭 if (Status.Open.equals(beforeStatus)) { if (Status.Close.equals(afterStatus)) { ActivityService.execStatus(activityId, beforeStatus, afterStatus); return new Result("0000", "变更状态成功"); } else { return new Result("0001", "变更状态拒绝"); } } return new Result("0001", "非可处理的活动状态变更"); } }
ifelse
,基本这也是大部分初级程序员的开发方式。但基本不可能不迭代
)。而且随着状态和需求变化,会越来越难以维护,后面的人也不好看懂并且很容易填充其他的流程进去。越来越乱就是从点滴开始的
@Test public void test() { // 初始化数据 String activityId = "100001"; ActivityService.init(activityId, Status.Editing); ActivityExecStatusController activityExecStatusController = new ActivityExecStatusController(); Result resultRefuse = activityExecStatusController.execStatus(activityId, Status.Editing, Status.Refuse); logger.info("测试结果(编辑中To审核拒绝):{}", JSON.toJSONString(resultRefuse)); Result resultCheck = activityExecStatusController.execStatus(activityId, Status.Editing, Status.Check); logger.info("测试结果(编辑中To提交审核):{}", JSON.toJSONString(resultCheck)); }
编辑中
到审核拒绝
,另外一个是从编辑中到提交审核
。审核拒绝
的,这中间还需要提审
。23:24:30.774 [main] INFO org.itstack.demo.design.test.ApiTest - 测试结果(编辑中To审核拒绝):{"code":"0001","info":"变更状态拒绝"} 23:24:30.778 [main] INFO org.itstack.demo.design.test.ApiTest - 测试结果(编辑中To提交审核):{"code":"0000","info":"变更状态成功"} Process finished with exit code 0
接下来使用状态模式来进行代码优化,也算是一次很小的重构。
重构的重点往往是处理掉ifelse
,而想处理掉ifelse
基本离不开接口与抽象类,另外还需要重新改造代码结构。
itstack-demo-design-19-02 └── src └── main └── java └── org.itstack.demo.design ├── event │ ├── CheckState.java │ └── CloseState.java │ └── DoingState.java │ └── EditingState.java │ └── OpenState.java │ └── PassState.java │ └── RefuseState.java ├── Result.java ├── State.java └── StateHandler.java
状态模式模型结构
提审、审核、拒审等
)。if
语言进行判断了。StateHandler
对状态流程的统一处理,里面提供Map
结构的各项服务接口调用,也就避免了使用if
判断各项状态转变的流程。public abstract class State { /** * 活动提审 * * @param activityId 活动ID * @param currentStatus 当前状态 * @return 执行结果 */ public abstract Result arraignment(String activityId, Enum<Status> currentStatus); /** * 审核通过 * * @param activityId 活动ID * @param currentStatus 当前状态 * @return 执行结果 */ public abstract Result checkPass(String activityId, Enum<Status> currentStatus); /** * 审核拒绝 * * @param activityId 活动ID * @param currentStatus 当前状态 * @return 执行结果 */ public abstract Result checkRefuse(String activityId, Enum<Status> currentStatus); /** * 撤审撤销 * * @param activityId 活动ID * @param currentStatus 当前状态 * @return 执行结果 */ public abstract Result checkRevoke(String activityId, Enum<Status> currentStatus); /** * 活动关闭 * * @param activityId 活动ID * @param currentStatus 当前状态 * @return 执行结果 */ public abstract Result close(String activityId, Enum<Status> currentStatus); /** * 活动开启 * * @param activityId 活动ID * @param currentStatus 当前状态 * @return 执行结果 */ public abstract Result open(String activityId, Enum<Status> currentStatus); /** * 活动执行 * * @param activityId 活动ID * @param currentStatus 当前状态 * @return 执行结果 */ public abstract Result doing(String activityId, Enum<Status> currentStatus); }
活动ID
)、currentStatus(当前状态
),只有他们的具体实现是不同的。编辑
public class EditingState extends State { public Result arraignment(String activityId, Enum<Status> currentStatus) { ActivityService.execStatus(activityId, currentStatus, Status.Check); return new Result("0000", "活动提审成功"); } public Result checkPass(String activityId, Enum<Status> currentStatus) { return new Result("0001", "编辑中不可审核通过"); } public Result checkRefuse(String activityId, Enum<Status> currentStatus) { return new Result("0001", "编辑中不可审核拒绝"); } @Override public Result checkRevoke(String activityId, Enum<Status> currentStatus) { return new Result("0001", "编辑中不可撤销审核"); } public Result close(String activityId, Enum<Status> currentStatus) { ActivityService.execStatus(activityId, currentStatus, Status.Close); return new Result("0000", "活动关闭成功"); } public Result open(String activityId, Enum<Status> currentStatus) { return new Result("0001", "非关闭活动不可开启"); } public Result doing(String activityId, Enum<Status> currentStatus) { return new Result("0001", "编辑中活动不可执行活动中变更"); } }
提审
public class CheckState extends State { public Result arraignment(String activityId, Enum<Status> currentStatus) { return new Result("0001", "待审核状态不可重复提审"); } public Result checkPass(String activityId, Enum<Status> currentStatus) { ActivityService.execStatus(activityId, currentStatus, Status.Pass); return new Result("0000", "活动审核通过完成"); } public Result checkRefuse(String activityId, Enum<Status> currentStatus) { ActivityService.execStatus(activityId, currentStatus, Status.Refuse); return new Result("0000", "活动审核拒绝完成"); } @Override public Result checkRevoke(String activityId, Enum<Status> currentStatus) { ActivityService.execStatus(activityId, currentStatus, Status.Editing); return new Result("0000", "活动审核撤销回到编辑中"); } public Result close(String activityId, Enum<Status> currentStatus) { ActivityService.execStatus(activityId, currentStatus, Status.Close); return new Result("0000", "活动审核关闭完成"); } public Result open(String activityId, Enum<Status> currentStatus) { return new Result("0001", "非关闭活动不可开启"); } public Result doing(String activityId, Enum<Status> currentStatus) { return new Result("0001", "待审核活动不可执行活动中变更"); } }
checkRefuse
这个方法对于不同的类中有不同的实现,也就是不同状态下能做的下一步流转操作已经可以在每一个方法中具体控制了。public class StateHandler { private Map<Enum<Status>, State> stateMap = new ConcurrentHashMap<Enum<Status>, State>(); public StateHandler() { stateMap.put(Status.Check, new CheckState()); // 待审核 stateMap.put(Status.Close, new CloseState()); // 已关闭 stateMap.put(Status.Doing, new DoingState()); // 活动中 stateMap.put(Status.Editing, new EditingState()); // 编辑中 stateMap.put(Status.Open, new OpenState()); // 已开启 stateMap.put(Status.Pass, new PassState()); // 审核通过 stateMap.put(Status.Refuse, new RefuseState()); // 审核拒绝 } public Result arraignment(String activityId, Enum<Status> currentStatus) { return stateMap.get(currentStatus).arraignment(activityId, currentStatus); } public Result checkPass(String activityId, Enum<Status> currentStatus) { return stateMap.get(currentStatus).checkPass(activityId, currentStatus); } public Result checkRefuse(String activityId, Enum<Status> currentStatus) { return stateMap.get(currentStatus).checkRefuse(activityId, currentStatus); } public Result checkRevoke(String activityId, Enum<Status> currentStatus) { return stateMap.get(currentStatus).checkRevoke(activityId, currentStatus); } public Result close(String activityId, Enum<Status> currentStatus) { return stateMap.get(currentStatus).close(activityId, currentStatus); } public Result open(String activityId, Enum<Status> currentStatus) { return stateMap.get(currentStatus).open(activityId, currentStatus); } public Result doing(String activityId, Enum<Status> currentStatus) { return stateMap.get(currentStatus).doing(activityId, currentStatus); } }
itstack-demo-design-19-01
例子中还得传两个状态来判断。@Test public void test_Editing2Arraignment() { String activityId = "100001"; ActivityService.init(activityId, Status.Editing); StateHandler stateHandler = new StateHandler(); Result result = stateHandler.arraignment(activityId, Status.Editing); logger.info("测试结果(编辑中To提审活动):{}", JSON.toJSONString(result)); logger.info("活动信息:{} 状态:{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityInfo(activityId).getStatus())); }
测试结果
23:59:20.883 [main] INFO org.itstack.demo.design.test.ApiTest - 测试结果(编辑中To提审活动):{"code":"0000","info":"活动提审成功"} 23:59:20.907 [main] INFO org.itstack.demo.design.test.ApiTest - 活动信息:{"activityId":"100001","activityName":"早起学习打卡领奖活动","beginTime":1593694760892,"endTime":1593694760892,"status":"Check"} 状态:"Check" Process finished with exit code 0
@Test public void test_Editing2Open() { String activityId = "100001"; ActivityService.init(activityId, Status.Editing); StateHandler stateHandler = new StateHandler(); Result result = stateHandler.open(activityId, Status.Editing); logger.info("测试结果(编辑中To开启活动):{}", JSON.toJSONString(result)); logger.info("活动信息:{} 状态:{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityInfo(activityId).getStatus())); }
测试结果
23:59:36.904 [main] INFO org.itstack.demo.design.test.ApiTest - 测试结果(编辑中To开启活动):{"code":"0001","info":"非关闭活动不可开启"} 23:59:36.914 [main] INFO org.itstack.demo.design.test.ApiTest - 活动信息:{"activityId":"100001","activityName":"早起学习打卡领奖活动","beginTime":1593694776907,"endTime":1593694776907,"status":"Editing"} 状态:"Editing" Process finished with exit code 0
@Test public void test_Refuse2Doing() { String activityId = "100001"; ActivityService.init(activityId, Status.Refuse); StateHandler stateHandler = new StateHandler(); Result result = stateHandler.doing(activityId, Status.Refuse); logger.info("测试结果(拒绝To活动中):{}", JSON.toJSONString(result)); logger.info("活动信息:{} 状态:{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityInfo(activityId).getStatus())); }
测试结果
23:59:46.339 [main] INFO org.itstack.demo.design.test.ApiTest - 测试结果(拒绝To活动中):{"code":"0001","info":"审核拒绝不可执行活动为进行中"} 23:59:46.352 [main] INFO org.itstack.demo.design.test.ApiTest - 活动信息:{"activityId":"100001","activityName":"早起学习打卡领奖活动","beginTime":1593694786342,"endTime":1593694786342,"status":"Refuse"} 状态:"Refuse" Process finished with exit code 0
@Test public void test_Refuse2Revoke() { String activityId = "100001"; ActivityService.init(activityId, Status.Refuse); StateHandler stateHandler = new StateHandler(); Result result = stateHandler.checkRevoke(activityId, Status.Refuse); logger.info("测试结果(拒绝To撤审):{}", JSON.toJSONString(result)); logger.info("活动信息:{} 状态:{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityInfo(activityId).getStatus())); }
测试结果
23:59:50.197 [main] INFO org.itstack.demo.design.test.ApiTest - 测试结果(拒绝To撤审):{"code":"0000","info":"撤销审核完成"} 23:59:50.208 [main] INFO org.itstack.demo.design.test.ApiTest - 活动信息:{"activityId":"100001","activityName":"早起学习打卡领奖活动","beginTime":1593694810201,"endTime":1593694810201,"status":"Editing"} 状态:"Editing" Process finished with exit code 0
有效流转
和拒绝流转
,不同的状态服务处理不同的服务内容。ifelse
,代码的结构也更加清晰易于扩展。这就是设计模式的好处,可以非常强大的改变原有代码的结构,让以后的扩展和维护都变得容易些。单一职责
和开闭原则
,当你只有满足这样的结构下才会发现代码的扩展是容易的,也就是增加和修改功能不会影响整体的变化。1. 重学 Java 设计模式:实战工厂方法模式「多种类型商品不同接口,统一发奖服务搭建场景」
2. 重学 Java 设计模式:实战原型模式「上机考试多套试,每人题目和答案乱序排列场景」
3. 重学 Java 设计模式:实战桥接模式「多支付渠道(微信、支付宝)与多支付模式(刷脸、指纹)场景」
4. 重学 Java 设计模式:实战组合模式「营销差异化人群发券,决策树引擎搭建场景」
5. 重学 Java 设计模式:实战外观模式「基于SpringBoot开发门面模式中间件,统一控制接口白名单场景」
6. 重学 Java 设计模式:实战享元模式「基于Redis秒杀,提供活动与库存信息查询场景」
7. 重学 Java 设计模式:实战备忘录模式「模拟互联网系统上线过程中,配置文件回滚场景」