如何设计延迟执行机制:调用方法后待Execute指令触发再执行
延迟执行方法直到收到Execute指令的优雅设计
问题场景
我需要调用Method1、Method2、Method3时不立即执行内部逻辑,必须等调用Execute方法时才统一执行。核心原因是:在Execute执行前,如果其他代码抛出异常,我需要能执行回滚操作;要是提前运行这些方法,后续出现异常就来不及回滚了。现有初始代码结构如下:
public class FooService { public void Method1() { // RUN A } public void Method2() { // RUN B } public void Method3() { // RUN C } public void Execute() { // Execute all methods that being called } } public class BooService { public void Something() { Random r = new Random(); if (r.NextBool()) FooService.Method1(); if (r.NextBool()) FooService.Method2(); if (r.NextBool()) FooService.Method3(); //SOME SPECIAL CODE HERE FooService.Execute(); } }
现有解决方案的问题
你提供的T.S.解决方案存在几个明显问题:
- 静态
List<Action>存在线程安全隐患,多线程环境下容易出现数据错乱 Method3中错误判断了FooService.Method2,属于笔误,会导致逻辑错误- 方法内部的实际执行逻辑放在
else分支,逻辑颠倒——原本应该是“注册时不执行,Execute时才执行”,现在变成“重复注册时才执行” - 静态列表不会自动清空,多次调用会累积之前的操作,导致执行结果不符合预期
更优雅的改进方案
方案一:实例级动作列表(轻量简洁)
这个方案通过分离“注册动作”和“实际执行逻辑”,使用实例级存储避免线程问题,同时保证逻辑清晰:
public class FooService { private readonly List<Action> _pendingActions = new List<Action>(); // 注册要延迟执行的方法,不立即运行 public void RegisterMethod1() { // 避免重复注册同一方法 if (!_pendingActions.Contains(Method1Impl)) { _pendingActions.Add(Method1Impl); } } public void RegisterMethod2() { if (!_pendingActions.Contains(Method2Impl)) { _pendingActions.Add(Method2Impl); } } public void RegisterMethod3() { if (!_pendingActions.Contains(Method3Impl)) { _pendingActions.Add(Method3Impl); } } // 封装实际执行逻辑的私有方法 private void Method1Impl() { // RUN A } private void Method2Impl() { // RUN B } private void Method3Impl() { // RUN C } public void Execute() { try { foreach (var action in _pendingActions) { action.Invoke(); } } finally { // 执行后清空列表,防止重复执行 _pendingActions.Clear(); } } // 回滚方法:清空待执行动作,避免后续误执行 public void Rollback() { _pendingActions.Clear(); } } public class BooService { public void Something() { var fooService = new FooService(); Random r = new Random(); // 用Next(2)替代不存在的NextBool方法 if (r.Next(2) == 0) fooService.RegisterMethod1(); if (r.Next(2) == 0) fooService.RegisterMethod2(); if (r.Next(2) == 0) fooService.RegisterMethod3(); // SOME SPECIAL CODE HERE try { // 这里放可能抛出异常的风险代码 } catch (Exception) { fooService.Rollback(); throw; // 继续抛出异常,不吞掉 } fooService.Execute(); } }
方案优势:
- 实例级的
_pendingActions避免了线程安全问题,每个业务流程用独立的FooService实例 - 明确区分注册动作和执行逻辑,代码可读性更高
Execute后自动清空动作列表,防止重复执行- 提供
Rollback方法,在异常时可以快速清空待执行动作
方案二:命令模式(适合复杂场景)
如果后续需要扩展更多方法,或者每个方法需要独立的回滚逻辑,推荐使用命令模式,它能更好地封装执行与回滚逻辑:
// 命令接口,定义执行和回滚规范 public interface ICommand { void Execute(); void Rollback(); } // 每个方法对应一个命令类 public class Method1Command : ICommand { public void Execute() { // RUN A } public void Rollback() { // 这里写Method1执行后的回滚逻辑,比如删除新增数据、恢复状态 } } public class Method2Command : ICommand { public void Execute() { // RUN B } public void Rollback() { // Method2的回滚逻辑 } } public class Method3Command : ICommand { public void Execute() { // RUN C } public void Rollback() { // Method3的回滚逻辑 } } public class FooService { private readonly List<ICommand> _commands = new List<ICommand>(); // 添加对应命令到待执行列表 public void AddMethod1() { if (!_commands.OfType<Method1Command>().Any()) { _commands.Add(new Method1Command()); } } public void AddMethod2() { if (!_commands.OfType<Method2Command>().Any()) { _commands.Add(new Method2Command()); } } public void AddMethod3() { if (!_commands.OfType<Method3Command>().Any()) { _commands.Add(new Method3Command()); } } public void Execute() { foreach (var command in _commands) { command.Execute(); } _commands.Clear(); } public void Rollback() { // 反向执行回滚,保证回滚顺序正确(比如先执行的后回滚) foreach (var command in _commands.Reverse<ICommand>()) { command.Rollback(); } _commands.Clear(); } } public class BooService { public void Something() { var fooService = new FooService(); Random r = new Random(); if (r.Next(2) == 0) fooService.AddMethod1(); if (r.Next(2) == 0) fooService.AddMethod2(); if (r.Next(2) == 0) fooService.AddMethod3(); // SOME SPECIAL CODE HERE try { // 风险操作代码 } catch (Exception) { fooService.Rollback(); throw; } fooService.Execute(); } }
方案优势:
- 每个命令封装了执行和回滚逻辑,符合单一职责原则
- 扩展性极强,新增方法只需要实现
ICommand接口,无需修改FooService - 回滚时按反向顺序执行,保证数据操作的一致性
内容的提问来源于stack exchange,提问作者Mert
相关产品推荐
相关产品推荐

