在同一个班级中处理多个计时器的最佳方法是什么?

时间:2014-11-29 15:12:51

标签: c# .net architecture

我在课堂上使用了几个计时器,并想知道实现它的两个选项:

选项1:

每条消息都有自己的Timer。

 class MessagesManager1
{
    Timer _timerA = new Timer();
    Timer _timerB = new Timer();
    Timer _timerC = new Timer();

    public MessagesManager1()
    {
        _timerA.Interval = 1000; // 1 sec
        _timerB.Interval = 3000; // 3 sec
        _timerC.Interval = 5000; // 5 sec

        _timerA.Elapsed += _timerA_Elapsed;
        _timerB.Elapsed += _timerB_Elapsed;
        _timerC.Elapsed += _timerC_Elapsed;

        _timerA.Start();
        _timerB.Start();
        _timerC.Start();
    }

    void _timerA_Elapsed(object sender, ElapsedEventArgs e)
    {
        // send message A
    }

    void _timerB_Elapsed(object sender, ElapsedEventArgs e)
    {
        // send message B
    }

    void _timerC_Elapsed(object sender, ElapsedEventArgs e)
    {
        // send message C
    }
}

选项2: 仅使用一个计时器并发送每个消息取决于secondsCounter模数结果。

 class MessagesManager2
{
    Timer _timer = new Timer();

    public MessagesManager2()
    {
        _timer.Interval = 1000; // 1 sec

        _timer.Elapsed += _timer_Elapsed;
        _timer.Start();
    }

    int secondsCounter;

    void _timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        // send message A

        if (secondsCounter % 3 == 0)
        {
            // send message B
        }

        if (secondsCounter % 5 == 0)
        {
            // send message C
        }
    }
}

哪一个更好,为什么? (在性能,架构,维护,最佳实践等方面)。

1 个答案:

答案 0 :(得分:0)

我不会说那些。

你应该遵循单一责任原则 - 在你的两个选项中,同一个班级执行不止一件事。

我将创建一个具有单个Timer的类,然后在运行时创建该类的3个实例,并将间隔值传递给构造函数。

根据Elapsed事件中需要执行的操作,您可以将Action委托传递给构造函数,或者创建覆盖具有所需行为的抽象/虚拟方法的派生类。

方法1

如果您知道自己只需要处理这三件事,那么我会说最好在编译时将它们定义为显式行为:

public abstract class BaseTimerHandler
{
    private readonly Timer m_Timer;

    public BaseTimerHandler(int interval)
    {
        m_Timer = new Timer();
        m_Timer.Interval = interval;
        m_Timer.Elapsed += TimerElapsed;
        m_Timer.Start();
    }

    protected abstract TimerElapsed(object sender, ElapsedEventArgs e);

    //TODO unsubscribe from Elapsed in Dispose method.
}

public class TimerAHandler : BaseTimerHandler
{
    private const int _INTERVAL = 1000;
    public TimerAHandler() : base(_INTERVAL) { }

    protected override TimerElapsed(object sender, ElapsedEventArgs e)
    {
        //send message A
    }
}

public class TimerBHandler : BaseTimerHandler
{
    private const int _INTERVAL = 3000;
    public TimerBHandler() : base(_INTERVAL) { }

    protected override TimerElapsed(object sender, ElapsedEventArgs e)
    {
        //send message B
    }
}

public class TimerCHandler : BaseTimerHandler
{
    private const int _INTERVAL = 5000;
    public TimerCHandler() : base(_INTERVAL) { }

    protected override TimerElapsed(object sender, ElapsedEventArgs e)
    {
        //send message C
    }
}

方法2

如果你正在谈论拥有可变数量的这些定时器,可能是在运行时定义的,那么我会注入行为而不是定义显式类(如果你需要发送者,可以调整Action委托以包含参数事件args)。

public class TimerHandler
{
    private readonly Timer m_Timer;
    private readonly Action m_Elapsed;

    public BaseTimerHandler(int interval, Action sendMessage)
    {
        if (sendMessage == null)
            throw new ArgumentNullException("sendMessage", "Must provide send message");

        m_Timer = new Timer();
        m_Timer.Interval = interval;
        m_Timer.Elapsed += TimerElapsed;

        m_Elapsed = sendMessage;

        m_Timer.Start();
    }

    private TimerElapsed(object sender, ElapsedEventArgs e){
    {
        m_Elapsed();
    }

    //TODO unsubscribe from Elapsed in Dispose method.
}

var timerA = new TimerHandler(1, () => { //send message A });
var timerB = new TimerHandler(3, () => { //send message B });
var timerC = new TimerHandler(5, () => { //send message B });

这看起来有点像矫枉过正,但我​​假设你的例子是简化的。在这两种情况下,优点是:

  • 您可以单独测试定时器处理与要在Elapsed中执行的实际代码(在BaseTimerHandler中您可以为单元测试创​​建Mock派生类型,在TimerHandler中可以注入假Action
  • 您已删除了定时器代码的重复,因此更易于维护并确保一致性。
  • 如果要在Elapsed中执行的代码也包含一些公共代码,那么您可以将BaseTimerHandler.TimerElapsed扩展为虚拟方法并添加此公共代码,或者对于TimerHandler,展开TimerElapsed以包含公共代码