CodeGuide 实战解析:用状态模式重构营销活动审核状态流转,告别 ifelse
文档教程后端【免费下载链接】CodeGuide:books: 本代码库是作者小傅哥多年从事一线互联网 Java 开发的学习历程技术汇总旨在为大家提供一个清晰详细的学习教程侧重点更倾向编写Java核心内容。如果本仓库能为您提供帮助请给予支持(关注、点赞、分享)项目地址https://gitcode.com/gh_mirrors/code/CodeGuide点击查看免费下载本文以 CodeGuide 仓库《重学 Java 设计模式》系列中的状态模式章节为主体以系统营销活动审核发布上线这一真实业务场景为骨架完整讲解状态模式的核心思想、基于 ifelse 的原始实现、以及基于接口 抽象类 状态机映射的状态模式重构方案并通过多组单元测试验证状态流转的合法性。读完本文你将掌握如何用状态模式消除多分支状态判断、如何设计State抽象类与StateHandler状态处理器以及判断什么场景值得引入状态模式的权衡方法。一、状态模式介绍行为随状态而变状态模式State Pattern描述的是一种行为下的多种状态变更。它允许一个对象在其内部状态改变时改变它的行为对象看起来像是修改了它的类。举一个最常见的生活化例子网站的页面在你登录与不登录两种状态下展示的内容是略有差异的——不登录不能展示个人信息。这种登录 / 不登录就是通过改变状态让整个页面的行为发生了变化。再比如 80 后、90 后都用过的磁带放音机放入磁带后通过机器上的一排按钮可以让放音机播放磁带内容而且有些按钮是互斥的——只有在某个状态下才可以按另外的按钮。这种不同状态下可执行的操作不同的约束正是状态模式要解决的核心问题。对应到代码设计上状态模式把某个状态能做什么、不能做什么从大段的判断逻辑中抽离出来交给一个个独立的状态实现类去表达从而让状态流转规则变得清晰、可扩展、可维护。二、案例场景模拟营销活动审核状态流转在本案例中我们模拟一个营销活动审核状态流转场景——一个活动的上线需要经过多个层级的审核。流程节点中包括各个状态到下一个状态扭转的关联条件例如只有审核通过才能进入活动中而不能从编辑中直接到活动中。这些状态之间的转变就是本案例要完成的场景处理。大部分程序员基本都开发过类似的业务场景对活动或一些配置需要审核后才能对外发布而这个审核的过程往往会随着系统的重要程度而设立多级控制来保证一个活动可以安全上线避免造成资损。当然有时也会使用到一些审批流的配置来方便开发可以在配置中设定某个节点的审批人员但这并不是本案例要体现的重点——本案例主要模拟学习对一个活动的多个状态节点的审核控制。在本仓库中本文属于 《重学 Java 设计模式》系列 中的一节与工厂方法模式、策略模式、模板模式等同属一个完整的实战教学体系该系列图书信息可参考 《重学Java设计模式》书籍介绍。1. 场景模拟工程结构围绕这个场景原文搭建了一个场景模拟工程工程结构如下itstack-demo-design-19-00 └── src └── main └── java └── org.itstack.demo.design ├── ActivityInfo.java ├── Status.java └── ActivityService.java在这个模拟工程里提供了三个类状态枚举Status、活动对象ActivityInfo、活动服务ActivityService。2. 基本活动信息public class ActivityInfo { private String activityId; // 活动ID private String activityName; // 活动名称 private EnumStatus status; // 活动状态 private Date beginTime; // 开始时间 private Date endTime; // 结束时间 // ...get/set }这是一些基本的活动信息活动 ID、活动名称、活动状态、开始时间、结束时间。3. 活动枚举状态public enum Status { // 1创建编辑、2待审核、3审核通过(任务扫描成活动中)、4审核拒绝(可以撤审到编辑状态)、5活动中、6活动关闭、7活动开启(任务扫描成活动中) Editing, Check, Pass, Refuse, Doing, Close, Open }活动的枚举共 7 个状态对应含义如下枚举值状态含义说明Editing创建编辑活动初始状态Check待审核编辑后提交审核Pass审核通过由任务扫描成活动中Refuse审核拒绝可以撤审回到编辑状态Doing活动中活动正在执行Close活动关闭关闭后不可直接开启Open活动开启由任务扫描成活动中4. 活动服务接口public class ActivityService { private static MapString, EnumStatus statusMap new ConcurrentHashMapString, EnumStatus(); public static void init(String activityId, EnumStatus 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 EnumStatus queryActivityStatus(String activityId) { return statusMap.get(activityId); } /** * 执行状态变更 * * param activityId 活动ID * param beforeStatus 变更前状态 * param afterStatus 变更后状态 */ public static synchronized void execStatus(String activityId, EnumStatus beforeStatus, EnumStatus afterStatus) { if (!beforeStatus.equals(statusMap.get(activityId))) return; statusMap.put(activityId, afterStatus); } }在这个静态类中提供了活动的查询和状态变更接口queryActivityInfo、queryActivityStatus、execStatus。同时使用Map结构来记录活动 ID 和状态变化信息另外还有init方法来初始化活动数据。需要说明的是这里为了便于演示使用了内存 Map 模拟数据存储实际的开发中这类信息基本都是从数据库或者Redis中获取execStatus方法使用synchronized保证状态变更的原子性并做了前置状态校验beforeStatus必须与当前存储状态一致才允许变更这本身就是一个轻量级的乐观校验。三、用一坨坨代码实现ifelse 的代价这里先使用最粗暴的方式来实现功能。对于这样各种状态的变更最直接想到的就是使用if和else进行判断处理。每一个状态可以流转到下一个什么状态都可以使用嵌套的if实现。1. 工程结构itstack-demo-design-19-01 └── src └── main └── java └── org.itstack.demo.design ├── ActivityExecStatusController.java └── Result.java整个实现的工程结构比较简单只包括了两个类ActivityExecStatusController处理流程状态、Result返回结果对象。2. 代码实现整篇 ifelsepublic class ActivityExecStatusController { /** * 活动状态变更 * 1. 编辑中 - 提审、关闭 * 2. 审核通过 - 拒绝、关闭、活动中 * 3. 审核拒绝 - 撤审、关闭 * 4. 活动中 - 关闭 * 5. 活动关闭 - 开启 * 6. 活动开启 - 关闭 * * param activityId 活动ID * param beforeStatus 变更前状态 * param afterStatus 变更后状态 * return 返回结果 */ public Result execStatus(String activityId, EnumStatus beforeStatus, EnumStatus 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这也是大部分初级程序员的开发方式。综合来看这套代码中的状态流转规则是编辑中 → 提审、关闭审核通过 → 拒绝、关闭、活动中审核拒绝 → 撤审、关闭活动中 → 关闭活动关闭 → 开启活动开启 → 关闭这样的面向过程式开发方式对于不需要改动代码、也不需要二次迭代的场景还是可以使用的但基本不可能不迭代。随着状态和需求的变化这套代码会越来越难以维护每增加一个状态就要在ifelse链条中再插入一段判断后来的人不好看懂也很容易填充其他流程进去。越来越乱就是从点滴开始的。3. 测试验证 ifelse 实现3.1 编写测试类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)); }测试代码包括两个功能的验证一个是从编辑中到审核拒绝另外一个是从编辑中到提交审核。因为从场景流程中可以看到编辑中的活动是不能直接到审核拒绝的这中间还需要提审。3.2 测试结果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基本离不开接口与抽象类另外还需要重新改造代码结构。1. 工程结构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状态模式模型结构State是一个抽象类定义了各种操作接口提审、审核、拒审等右侧的 7 个状态实现类与场景中的状态一一对应是各种状态流转的实现操作。这里实现的关键点在于每一种状态到下一个状态都分配到各个实现方法中控制也就不需要if语言进行判断了。最后是StateHandler对状态流程的统一处理内部提供Map结构的各项服务接口调用也就避免了使用if判断各项状态转变的流程。2. 定义状态抽象类public abstract class State { /** * 活动提审 * * param activityId 活动ID * param currentStatus 当前状态 * return 执行结果 */ public abstract Result arraignment(String activityId, EnumStatus currentStatus); /** * 审核通过 * * param activityId 活动ID * param currentStatus 当前状态 * return 执行结果 */ public abstract Result checkPass(String activityId, EnumStatus currentStatus); /** * 审核拒绝 * * param activityId 活动ID * param currentStatus 当前状态 * return 执行结果 */ public abstract Result checkRefuse(String activityId, EnumStatus currentStatus); /** * 撤审撤销 * * param activityId 活动ID * param currentStatus 当前状态 * return 执行结果 */ public abstract Result checkRevoke(String activityId, EnumStatus currentStatus); /** * 活动关闭 * * param activityId 活动ID * param currentStatus 当前状态 * return 执行结果 */ public abstract Result close(String activityId, EnumStatus currentStatus); /** * 活动开启 * * param activityId 活动ID * param currentStatus 当前状态 * return 执行结果 */ public abstract Result open(String activityId, EnumStatus currentStatus); /** * 活动执行 * * param activityId 活动ID * param currentStatus 当前状态 * return 执行结果 */ public abstract Result doing(String activityId, EnumStatus currentStatus); }在整个接口中提供了各项状态流转服务的抽象方法例如活动提审、审核通过、审核拒绝、撤审撤销等 7 个方法。在这些方法中所有的入参都是一样的——activityId活动ID、currentStatus当前状态——只有它们的具体实现是不同的。这样一来状态流转的合法性规则被下沉到了每个状态实现类内部天然形成了该状态允许哪些操作、拒绝哪些操作的边界。3. 部分状态流转实现编辑状态EditingStatepublic class EditingState extends State { public Result arraignment(String activityId, EnumStatus currentStatus) { ActivityService.execStatus(activityId, currentStatus, Status.Check); return new Result(0000, 活动提审成功); } public Result checkPass(String activityId, EnumStatus currentStatus) { return new Result(0001, 编辑中不可审核通过); } public Result checkRefuse(String activityId, EnumStatus currentStatus) { return new Result(0001, 编辑中不可审核拒绝); } Override public Result checkRevoke(String activityId, EnumStatus currentStatus) { return new Result(0001, 编辑中不可撤销审核); } public Result close(String activityId, EnumStatus currentStatus) { ActivityService.execStatus(activityId, currentStatus, Status.Close); return new Result(0000, 活动关闭成功); } public Result open(String activityId, EnumStatus currentStatus) { return new Result(0001, 非关闭活动不可开启); } public Result doing(String activityId, EnumStatus currentStatus) { return new Result(0001, 编辑中活动不可执行活动中变更); } }提审状态CheckStatepublic class CheckState extends State { public Result arraignment(String activityId, EnumStatus currentStatus) { return new Result(0001, 待审核状态不可重复提审); } public Result checkPass(String activityId, EnumStatus currentStatus) { ActivityService.execStatus(activityId, currentStatus, Status.Pass); return new Result(0000, 活动审核通过完成); } public Result checkRefuse(String activityId, EnumStatus currentStatus) { ActivityService.execStatus(activityId, currentStatus, Status.Refuse); return new Result(0000, 活动审核拒绝完成); } Override public Result checkRevoke(String activityId, EnumStatus currentStatus) { ActivityService.execStatus(activityId, currentStatus, Status.Editing); return new Result(0000, 活动审核撤销回到编辑中); } public Result close(String activityId, EnumStatus currentStatus) { ActivityService.execStatus(activityId, currentStatus, Status.Close); return new Result(0000, 活动审核关闭完成); } public Result open(String activityId, EnumStatus currentStatus) { return new Result(0001, 非关闭活动不可开启); } public Result doing(String activityId, EnumStatus currentStatus) { return new Result(0001, 待审核活动不可执行活动中变更); } }这里提供了两个具体实现类编辑状态和提审状态。可以看到同一个checkRefuse方法在编辑状态下返回编辑中不可审核拒绝而在提审状态下则执行状态流转返回活动审核拒绝完成——这就是不同状态下能做的下一步流转操作可以在每一个方法中具体控制的直接体现。其余 5 个状态实现类CloseState、DoingState、OpenState、PassState、RefuseState的操作是类似的大部分是重复代码遵循同样的允许流转则调用ActivityService.execStatus并返回成功不允许流转则直接返回拒绝模式可以通过源码学习理解。4. 状态处理服务StateHandlerpublic class StateHandler { private MapEnumStatus, State stateMap new ConcurrentHashMapEnumStatus, 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, EnumStatus currentStatus) { return stateMap.get(currentStatus).arraignment(activityId, currentStatus); } public Result checkPass(String activityId, EnumStatus currentStatus) { return stateMap.get(currentStatus).checkPass(activityId, currentStatus); } public Result checkRefuse(String activityId, EnumStatus currentStatus) { return stateMap.get(currentStatus).checkRefuse(activityId, currentStatus); } public Result checkRevoke(String activityId, EnumStatus currentStatus) { return stateMap.get(currentStatus).checkRevoke(activityId, currentStatus); } public Result close(String activityId, EnumStatus currentStatus) { return stateMap.get(currentStatus).close(activityId, currentStatus); } public Result open(String activityId, EnumStatus currentStatus) { return stateMap.get(currentStatus).open(activityId, currentStatus); } public Result doing(String activityId, EnumStatus currentStatus) { return stateMap.get(currentStatus).doing(activityId, currentStatus); } }StateHandler是状态服务的统一控制中心在构造函数中建立了所有状态枚举到具体状态实现类的映射关系放到Map数据结构中使用ConcurrentHashMap保证并发安全。同时提供不同名称的接口操作类让外部调用方可以更加容易地使用功能接口——调用方只需要传入activityId和currentStatus而不需要像ifelse版本那样传两个状态来判断。这个Map映射正是状态模式的查表替代分支核心状态流转规则不再散落在 ifelse 链中而是集中收敛在状态 → 状态实现类的一张表 每个实现类内部的 7 个方法里。新增一个状态只需新增一个State子类并注册进stateMap无需改动任何已有状态类符合开闭原则。五、状态模式重构后的测试验证1. 测试类一编辑中 → 提审合法流转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测试编辑中 → 提审活动状态由Editing合法流转为Check。2. 测试类二编辑中 → 开启非法流转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测试编辑中 → 开启活动EditingState.open直接返回拒绝状态保持不变证明非法流转被有效拦截。3. 测试类三拒绝 → 活动中非法流转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测试拒绝 → 活动中被RefuseState.doing拦截证明审核被拒绝的活动不能直接进入进行中状态。4. 测试类四拒绝 → 撤审合法流转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测试拒绝 → 撤审状态由Refuse合法流转回Editing。综上以上四个测试类分别模拟了不同状态之间的有效流转和拒绝流转不同的状态服务处理不同的服务内容。这组测试也印证了状态模式重构后的可验证性每一个状态 × 操作组合的行为都是确定且可独立测试的这是面向过程 ifelse 版本难以做到的。六、总结与适用边界从以上两种方式对同一个需求的实现可以看到在使用状态模式处理后消灭了 ifelse代码中已经没有了多层嵌套的状态判断代码的结构更加清晰、易于扩展。状态流转的合法性从调用方的判断迁移到了状态实现类自身的约束职责更加内聚。从面向过程走向面向对象实现结构上不再是面向过程的编程而是面向对象的结构。这样的设计模式满足了单一职责原则每个状态类只负责自身状态的流转规则和开闭原则新增状态只需新增子类并注册无需修改已有类只有满足这样的结构下才会发现代码的扩展是容易的——增加和修改功能不会影响整体的变化。权衡成本如果状态和各项流转较多像本文案例中就会产生较多的实现类因此可能会带来代码实现上的时间成本。遇到这样的场景可以按需评估投入产出比主要判断点在于是否经常修改、是否可以做成组件化、能否抽离业务与非业务功能。对于类似多层级审核上线订单状态机审批流这类状态多、流转规则复杂且频繁迭代的业务场景状态模式是值得优先考虑的重构方案而如果状态极少且规则几乎不变则可以权衡是否值得引入较多的实现类。本案例完整的代码结构、类图与测试用例可结合 CodeGuide 仓库 中《重学 Java 设计模式》系列的其余篇章如责任链模式、命令模式对照学习理解不同模式在消除条件分支时的不同切入角度。赞分享文档教程后端【免费下载链接】CodeGuide:books: 本代码库是作者小傅哥多年从事一线互联网 Java 开发的学习历程技术汇总旨在为大家提供一个清晰详细的学习教程侧重点更倾向编写Java核心内容。如果本仓库能为您提供帮助请给予支持(关注、点赞、分享)项目地址https://gitcode.com/gh_mirrors/code/CodeGuide点击查看免费下载相关推荐itstack-demo-design状态模式活动生命周期管理实战状态机让状态流转不再靠硬编码itstack demo design状态模式活动生命周期管理实战状态机让状态流转不再靠硬编码 在《重学Java设计模式》实战项目 itstack demo示例工程教程告别状态混乱Temporal工作流状态管理实战指南告别状态混乱Temporal工作流状态管理实战指南 你是否还在为复杂业务流程中的状态同步而头疼订单处理中断、数据不一致、重试逻辑混乱本文将带你了解Temp后端工作流自动化任务调度CANN/GE图引擎UpdateOutputDesc函数UpdateOutputDesca nameZH CN_TOPIC_0000002484276834 /a 产品支持情况a namesectio人工智能深度学习模型编译模型优化编译器Ascend上一篇摄影工作流革命semi-utils批量水印工具的完整解决方案下一篇Monitorian如何优雅管理Windows多显示器亮度控制创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

loop-engineering 的 loop-triage 技能:用 AI Agent 构建每日优先级的工程分诊循环

loop-engineering 的 loop-triage 技能:用 AI Agent 构建每日优先级的工程分诊循环

人工智能AI AgentAgent 工作流CLI研发协作AI 技能MCP 服务 【免费下载链接】loop-engineering Practical patterns, starters & CLI tools for loop engineering with AI coding agents. Design systems that prompt and orchestrate agents (inspired by Addy Osmani and …

2026/9/24 16:23:28 阅读更多 →
FPGA与以太网(基本都能实现1Gbit/s就用LWIP实现最高效虽然达不到纯FPGA的效率)

FPGA与以太网(基本都能实现1Gbit/s就用LWIP实现最高效虽然达不到纯FPGA的效率)

一: 1.1:以太网数据帧(IEEE 802.3 标准规定) 1.2:千兆以太网:英文全称 Gigabit Ethernet,简称 GbE 或 1GE,是传输速率为 1 Gbit/s(1000 Mbit/s) 的以太网技…

2026/9/24 16:23:28 阅读更多 →
SAP IT mobiles 移动端菜单管理

SAP IT mobiles 移动端菜单管理

SAP IT mobiles 移动端菜单管理 日常运维当中有时候会用到。 设置的目录SPRO-后勤执行-移动数据输入-定义菜单管理 子菜单的对应的1,2,3,4,5,6应该最多是6个 菜单类型其实就是说是一个可执行的事务码,还是下一个菜单 具体要执行的事务码 显示的菜单名称

2026/9/24 16:23:28 阅读更多 →

最新新闻

基于 Java Spring Boot 的中学课外兴趣小组管理系统设计与实现

基于 Java Spring Boot 的中学课外兴趣小组管理系统设计与实现

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 1. 项目背景与意义 随着素质教育的深入推进,中学课外兴趣小组活动日益丰富,涵盖科技、艺术、体育、文学等多个方向。然而,传统的人工…

2026/9/24 16:58:09 阅读更多 →
Numba 浮点语义指南:精度差异、线性代数类型行为与 ufunc 错误处理

Numba 浮点语义指南:精度差异、线性代数类型行为与 ufunc 错误处理

编译器高性能计算 【免费下载链接】numba NumPy aware dynamic Python compiler using LLVM 项目地址: https://gitcode.com/gh_mirrors/nu/numba 点击查看 免费下载 本指南以 Numba 官方参考文档 浮点陷阱(Floating-point pitfalls) 为骨架…

2026/9/24 16:58:09 阅读更多 →
工业读码器在 SMT 产线追溯中的应用

工业读码器在 SMT 产线追溯中的应用

去年在东莞长安一家 SMT 代工厂调试产线追溯系统,客户的生产经理跟我吐槽:上个月一批 PCB 板出了质量问题,想追溯是哪天上的料、哪个班次干的,结果翻了半天 MES 系统,发现有将近 15% 的板号没扫进去。问题出在贴片前那…

2026/9/24 16:58:09 阅读更多 →
码识别常DPM见难点与解决方案

码识别常DPM见难点与解决方案

做工业读码这行,DPM 码是绕不开的硬骨头。汽车发动机缸体、航空紧固件、手机中框、轴承套圈…… 这些零件不能贴纸质标签,高温油污一蹭就没了,只能把码直接刻在金属表面。激光雕刻、化学蚀刻、气动打标,工艺不同,码的样…

2026/9/24 16:58:09 阅读更多 →
update_manifest.py 脚本逆向拆解:Cangjie Nightly Builds 的版本校验、SHA256 计算与回写自校验

update_manifest.py 脚本逆向拆解:Cangjie Nightly Builds 的版本校验、SHA256 计算与回写自校验

update_manifest.py 脚本逆向拆解:Cangjie Nightly Builds 的版本校验、SHA256 计算与回写自校验 【免费下载链接】nightly_build Cangjie Nightly Builds 版本归档,Nightly版本暂存时间为30天 项目地址: https://gitcode.com/Cangjie/nightly_build …

2026/9/24 16:58:09 阅读更多 →
零售数据分析:如何用用户行为数据把“转化率“从3%提到8%?

零售数据分析:如何用用户行为数据把“转化率“从3%提到8%?

做电商和零售的朋友,应该都对转化率这个词特别敏感。同样的流量,转化率3%和8%,业绩差的可不是一点半点。很多人转化率上不去,就知道瞎优化主图、改价格,折腾来折腾去,效果微乎其微。其实转化率不是靠感觉调…

2026/9/24 16:57:07 阅读更多 →

日新闻

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为…

2026/9/24 0:00:19 阅读更多 →
单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:19 阅读更多 →
C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/24 0:00:19 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →