【问题标题】:passing timer through dependency injection and configuring / starting a timer通过依赖注入传递计时器并配置/启动计时器
【发布时间】:2013-09-01 10:32:51
【问题描述】:

我在这里遇到一种情况,我需要在我的类中使用一个计时器,它根据时间间隔调用一些方法。

在正常情况下,我会实例化计时器并在构造函数本身中对其进行配置。但我想以依赖注入的方式来做。在构造函数中传递计时器很容易,但是为了将一个方法绑定到它的 OnTimeElapsed 是很棘手的,工厂实例化该类是否应该配置这个计时器?在不违反依赖注入原则的情况下,我应该如何继续。

谢谢

编辑1

好的,让我重新表述我真正想问的问题。
  1. 根据发布的视频 Misko
  2. 在他的其他谈话中(从不测试你的框架:.net 框架 在这里)
  3. in his code review guide 任何东西 超出构造函数中的字段分配被认为是错误的。

我实现依赖注入的主要目标是,我需要在特定的经过时间对某个方法是否被调用进行单元测试。

所以我的问题是:我应该在哪里绑定 OnTimerElapsed 事件? 我真的想在这里测试计时器吗? 我好像迷路了

请帮忙。

【问题讨论】:

  • 你为什么要注入一个计时器?我认为没有理由这样做......
  • @PatrykĆwiek 依赖注入说要求你想要的东西。我只是对这个东西很陌生,不知道这是否是个好主意。

标签: c# oop architecture dependency-injection software-design


【解决方案1】:

如果您的问题是,您想依赖注入一个计时器,以便稍后将事件绑定到,那应该很简单。

只需创建您的 Timer 类以遵循 ITimer 接口并在其上创建方法来执行您想要的操作。

public class Calendar
{
    public Calendar(ITimer timer)
    {
        // timer is the dependency injected timer
        timer.SetEvent(EventReminder, 3600);
    }

    public void EventReminder()
    {
        Console.Write("Hey, it's time for your appointment!");
    }

}

public interface ITimer
{
    void SetEvent(Action callbackMethod, int interval);
}

在这种情况下,您有一个日历应用程序,并且您希望您的应用程序有一个计时器。但是你并不关心计时器是如何工作的,甚至是什么类型的计时器(也许你想要一个在几分钟或几小时内工作的计时器,或者它以其他方式工作)。你只知道你想要一个计时器,所以你依赖注入一个。

您必须创建一个接口来定义计时器将做什么,因为尽管它不关心您使用哪个计时器,但它确实关心计时器的功能。在我们的例子中,计时器可以做一件事 - 将事件设置为在某个时间间隔后发生。

所以我们注入了定时器,但是为了能够使用它,或者设置它,我们使用接口来定义方法。我们永远不知道它在内部是如何工作的——我们甚至不知道它有一个 OnElapsedEvent,我们也不在乎。把这个留给计时器的创建者吧,我们只需要一种方法来完成任务,这就是上面的代码所演示的。

【讨论】:

    【解决方案2】:

    为依赖注入的新手发布答案。

    我之前的有问题的代码放在这里:

    public class MyClassInvoker:IDisposable
    {
        readonly Timer _myTimer;
        readonly MyClass _myclass;
        public MyClassInvoker(Timer myTimer, MyClass myclass)
        {
            _myTimer = myTimer;
            _myclass = myclass;
            _myTimer.Interval = 3000;//configure Your timer here
            _myTimer.Elapsed +=new ElapsedEventHandler(PeriodicInvoker); 
        }
    
        public void Start()
        {
            _myTimer.Start();
        }
    
        public void Dispose()
        {
            _myTimer.Dispose();
        }
    
        void PeriodicInvoker(object sender, EventArgs e)
        {
            _myclass.DoSomePeriodicWork();
        }
    }
    

    修改后的代码如下:

    public class MyClassInvoker:IDisposable
    {
        readonly Timer _myTimer;
        readonly MyClass _myclass;
        public MyClassInvoker(MyClass myclass)
        {
            _myTimer = new Timer();
            _myclass = myclass;
    
        }
    
        public void Start()
        {
            _myTimer.Interval = 3000;//configure Your timer here
            //add or remove any previous listeners 
            //here depending upon the business needs
    
            _myTimer.Elapsed += new ElapsedEventHandler(PeriodicInvoker); 
            _myTimer.Start();
        }
    
        public void Dispose()
        {
            _myTimer.Dispose();
        }
    
        void PeriodicInvoker(object sender, EventArgs e)
        {
            _myclass.DoSomePeriodicWork();
        }
    }
    

    在直观地思考问题后,我发布分析:

    1. 根据我自己的问题中发布的链接,永远不要在 除了简单的赋值之外的构造函数。我实际上是在绑定一个 构造函数中的事件处理程序。 我现在无法评论什么 如果我这样做,可能会产生影响。
    2. 我在之前的代码中传递了一个Timer对象,以促进 可测试性。

    有解释的解决方案

    1. 通过将 Timer 作为依赖项传递,我试图给出一个 模拟 .Net Timer BCL 的接口,以便进行单元测试并进行替换。目前.Net中的Timer分为三种

      • System.Threading.Timer
      • System.Timers.Timer
      • System.Windows.Forms.Timer

      这些定时器都不共享同一个接口,所以 将它们作为依赖项传递是没有意义的,我已经更正了 在我修改后的代码中。

    2. 如果从逻辑上考虑,Start 方法会在之后调用 实例化 MyClassInvoker 类,所以如果我把事件绑定 那里会很有意义。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-11-08
      • 1970-01-01
      • 2017-05-02
      • 1970-01-01
      • 1970-01-01
      • 2021-02-26
      • 2021-08-13
      • 1970-01-01
      相关资源
      最近更新 更多