4

I'm currently refactoring an application which periodically downloads content from various sources (http, ftp, sql, etc). There is a schedule which controls the times during which the application can be active. For example, it can download content between 8AM and 16PM. The application has been written in an object oriented language (C#).

This is the general design I came up with in my first iteration: First iteration design

The Scheduler class will be responsible for keeping to the general schedule. It starts the downloading at the start of the scheduled period, and stops it at the end. The Scheduler contains a number of ITask implementations, each of which have their own bit of downloading to do. I've created an abstract base class implementation which periodically calls the protected abstract method "StartDownload". Subclasses of Task will only implement this method, and won't have to worry about timing and scheduling.

So far, so good. But while TDD'ing the Task baseclass, I realised that it was actually difficult to mock the behaviour of StartDownload. When the Task's timer ticks, it should only call StartDownload if it has finished the previous download iteration. But since these are implementation details, it's hard to mock.

This made me wonder if the Task class isn't actually violating the Single Responsibility Principle. After all, it's taking care of the periodical invocation of StartDownload. And the StartDownload method is responsible for the actual downloading. So I came up with a more separated design: Second iteration design

Here the responsibility of the Task class is limited to just periodically calling the Client. And the Client's only responsibility is downloading content. Testing the Task class will now be easier, because I can just inject an IDownloadClient mock. The Task and Download have a 1 to 1 relationship. So each Task performs one download, so to speak. In practice, all Task instances will be added to a single Scheduler instance and started/stopped from there.

I do wonder if this is actually a clearer design though... The Task class now seems a bit odd, being just a single implementation without any subclasses. What do you guys think?

migrated from codereview.stackexchange.com Apr 15 '14 at 13:05

This question came from our site for peer programmer code reviews.

  • How is it hard to test the Task class? Also, what is that LastDownload and NextDownload on ITask? – Euphoric Apr 15 '14 at 13:29
  • The Task class is pretty easy to test, except for one small thing: There's a requirement that says when its timer ticks and the task is still downloading, it should not start another download. The abstract "StartDownload" method should "report" when the downloading finishes. Since this is all internal behavior on the subclasses, it's tricky to test in the base class. LastDownload and NextDownload are just DateTime values used in the UI. Perhaps they should be moved to a ViewModel class. – PJanssen Apr 15 '14 at 14:08
  • How is it tricky? Just derive your mock class from Task, pretend it is still downloading and check if StartDownload is called when you call Start on Task. – Euphoric Apr 15 '14 at 14:17
  • I'd go with 1.I don't think there's any good reason for ITask other than to convolute the design. The abstract base Task class is sufficient, except I would change LastDownload/NextDownload to LastRunTime/NextRunTime, call the class something else and make Start/Stop abstract. Then you can derive from Task for any specific functionality that the Scheduler kicks off. I guess I'm not seeing the difficulty in testing that you are describing. Surely you can use Dependency Injection to pass in your comms class to the derived Task class instance. You need to mock the comms interface to test anyways. – Dunk Apr 15 '14 at 14:44
  • I agree about the ITask interface. That came about through TDD'ing the Scheduler class, before making the Task class. Start/Stop actually can't be abstract, since they contain logic that periodically calls StartDownload. That's actually the reason for having the base class in the first place. I could however move this to the Scheduler class, and make the Task classes purely responsible for the downloading. So that would be a combination of 1 and 2. – PJanssen Apr 15 '14 at 16:17
2

This is basically question of Composition vs. inheritance. In your first case, you use inheritance as way to share behavior. In second instance, you use composition. Also, in second case, it is easy to identify a Strategy pattern in IDownloadClient. So some would say the second case is better.

But there is also KISS and YAGNI. Your second case is already getting complicated for function it is supposed to do. It is questionable if you really need such abstract design.

If it was me, I would use the first one, but keep the second one in mind if more requirements come in, that might require complication of the whole design.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.