You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何设计延迟执行机制:调用方法后待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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 20:15:00