Gary McLean Hall

Software with Agility

I, Interface

Let’s take a simple interface and see what we can do with it.

public interface ITask()
{
    void Do();
}

This is pretty much as simple as it gets- a single void method which is parameterless. It models a task inasmuch as you can ‘do’ a task. That’s all this interface is fussed with: providing clients the ability to do something.

Given this very simple interface, we can exercise all sorts of patterns on it and furnish it with even more power through flexibility.

Null

One of the most useful things we can do is have clients behave correctly when given a null ITask reference. Ordinarily, the client would either have to test manually that the task is not a null reference, or it would throw a NullReferenceException. Through diligently substituting an instance of a special ITask implementation, instead of null, we can ensure that we have no more NullReferenceExceptions:

public TaskNull : ITask
{
    public void Do()
    {

    }
}

That’s all there is to it. Perhaps it could be a bit more intelligent (logging that a null task was attempted to be done, for example), but as long as we are sure to return an instance of this class rather than the null reference each time it is applicable, clients will not throw.

Compose

Next up, we can compose many tasks into this:

public class TaskComposite : ITask
{
    public TaskComposite(params ITask[] tasks)
    {
        this.composedTasks = new List<ITask>();
        if(tasks != null)
        {
            foreach(var task in tasks)
            {
                AddTask(task);
            }
        }
    }

    public void Do()
    {
        foreach(var task in this.composedTasks)
        {
            task.Do();
        }
    }

    public void AddTask(ITask task)
    {
         if(task != null)
         {
             this.composedTasks.Add(task);
         }
    }

    private List<ITask> composedTasks;
}

So, we can group a load of tasks together to be executed serially.  TaskComposite implements the ITask interface, so it can be passed into any constructor or method that requires an ITask and will execute all of its composed tasks when the client asks to Do the task. There’s also nothing to stop us from passing a TaskComposite to a TaskComposite, thereby creating a complex graph of tasks. When executed, the behavior is deterministic: the tasks will be done in depth-first order.

Predicate

public TaskPredicated : ITask
{
    public TaskPredicated(Func<bool> predicate, ITask task)
    {
        if(predicate == null)
        {
            throw new ArgumentException("predicate");
        }

        if(task == null)
        {
            throw new ArgumentException("task");
        }

        this.taskShouldBeDone = predicate;
        this.decoratedTask = decoratedTask;
    }

    public Do()
    {
        if(this.taskShouldBeDone())
        {
            this.decoratedTask.Do();
        }
    }

    private readonly Func<bool> taskShouldBeDone;
    private readonly ITask decoratedTask;
}

By wrapping an ITask in this decorator, you can have the task done conditionally, depending on the outcome of an external predicate*. Again, notice that this class is an ITask. This ITask can even be passed to the TaskComposite as one of many tasks. Now we have the ability to treat many ITask implementations as if it were one ITask, where some may or may not be executed, depending on some contextual predicate.

Branch

By extrapolating the predicate example, we can also elect to execute something on the false branch of the if-statement:

public TaskBranched : ITask
{
    public TaskBranched(Func<bool> predicate, ITask trueTask, ITask falseTask)
    {
        if(predicate == null)
        {
            throw new ArgumentException("predicate");
        }

        if(trueTask == null)
        {
            throw new ArgumentException("trueTask");
        }

        if(falseTask == null)
        {
            throw new ArgumentException("falseTask");
        }
        this.taskShouldBeDone = predicate;
        this.trueTask = trueTask;
        this.falseTask = falseTak;
    }

    public Do()
    {
        if(this.taskShouldBeDone())
        {
            this.trueTask.Do();
        }
        else
        {
            this.falseTask.Do();
        }
    }

    private readonly Func<bool> taskShouldBeDone;
    private readonly ITask trueTask;
    private readonly ITask falseTask;
}

Interrupt

How about the scenario where you want to do task A unless some event occurs and you then want to do task B?

First of all, we’ll need to pass an event into the constructor of this TaskInterruptible, but .NET doesn’t let us do that, so let’s model it as a trivial interface.

public interface IInterrupt
{
    event Action Signal;
}

We will need a class to implement this in order to adapt some other event. Imagine, for example, registering for a mouse-click, key-press, network IO or NServiceBus message and then firing the Signal event in response. This keeps our TaskInterruptible free from code specifically to handle those events.

public class TaskInterruptible : ITask
{
    public TaskInterruptible(IInterrupt interrupt, ITask normalTask, ITask interruptTask)
    {
        if(interrupt == null)
        {
            throw new ArgumentException("interrupt");
        }
        if(normalTask == null)
        {
            throw new ArgumentException("normalTask");
        }
        if(interruptTask == null)
        {
            throw new ArgumentException("interruptTask");
        }
        this.interrupt = interrupt;
        this.normalTask = normalTask;
        this.interruptTask = interruptTask;
    }

    public void Do()
    {
        this.interrupt.Signal += DoInterruptTask;
        this.normalTask.Do();
    }

    private void DoInterruptTask()
    {
        this.interruptTask.Do();
    }
}

Hmm, it’s a great start, but I’m not wholly happy with this yet. There’s a large possibility that these two tasks – normal task and interrupt task – are mutually exclusive and should not be running concurrently. This introduces a new concept – that a task can be cancellable. Are all tasks necessarily cancellable? If so, then we can add a new method onto the ITask interface. However, in the interest of the interface segregation principle I’m going to say that not all tasks are cancellable – some might elect to be, some might not. This, then, gives us a new interface:

public interface ICancellable
{
    void Cancel();
}

Again, this interface is really nice and simple. Let’s provide another decorator around ITask that will cancel something before running the wrapped task:

public interface TaskCancel : ITask
{
    public TaskCancel(ICancellable objectToCancel, ITask taskToRun)
    {
        if(objectToCancel == null)
        {
            throw new ArgumentException("objectToCancel");
        }
        if(taskToRun == null)
        {
            throw new ArgumentException("taskToRun");
        }
        this.objectToCancel = objectToCancel;
        this.taskToRun = taskToRun;
    }

    public void Do()
    {
        this.objectToCancel.Cancel();
        this.taskToRun.Run();
    }

    private readonly ICancellable objectToCancel;
    private readonly ITask taskToRun;
}

While I’m not sold on the name of this implementation, I’m pretty happy that I can now cancel anything that needs to be, before running a task.  As long as it implements ICancellable, I can freely pass it in as the first argument. There’s nothing to stop me from passing the same task in as both parameters – effectively restarting a task!

Now, I can pass an instance of this class as the interrupt task above, providing it with the normal task as the ICancellable (given that it is cancellable, of course) and I can rest assured that the previous task will be cancelled before running the new task. Lovely.

Thread

Some tasks might take a long time to execute. Given that all sorts of things can now be happening when you execute an ITask, it may be important to run a task on a separate thread. For instance, if a composite task contains three different tasks, the first of which takes a minute to complete, the other two tasks are just going to wait until that task is complete because they are executed serially on the same thread.

public interface TaskThreaded : ITask
{
    public TaskThreaded(ITask task)
    {
        if(task == null)
        {
            throw new ArgumentException("task");
        }
        this.taskToThread = task;
        this.backgroundWorker = new BackgroundWorker();
        this.backgroundWorker.DoWork += (sender, eventArgs) => DoTask();
    }

    public void Do()
    {
         this.backgroundWorker.RunWorkerAsync();
    }

    private void DoTask()
    {
        this.taskToThread.Do();
    }

    private readonly ITask taskToThread;
    private readonly BackgroundWorker backgroundWorker;
}

Obviously, the usual threading caveats apply to this: if concurrent tasks use common, volatile, memory then this needs to be managed carefully.

However, if the tasks are totally independent like in our hypothetical example, the first, long-running, task can be wrapped in this TaskThreaded decorator before being added to the composite. When run, the call to Do() will last only as long as it takes to start the new thread, with any subsequent tasks then running in parallel.

It’s also not a stretch to imagine that these threaded tasks are also, in fact, cancellable…

public interface TaskAsync : ITask, ICancellable
{
    public TaskAsync(ITask task)
    {
        if(task == null)
        {
            throw new ArgumentException("task");
        }
        this.taskToThread = task;
        this.backgroundWorker = new BackgroundWorker();
        this.backgroundWorker.DoWork += (sender, eventArgs) => DoTask();
        this.backgroundWorker.WorkerSupportsCancellation = true;
    }

    public void Do()
    {
         this.backgroundWorker.RunWorkerAsync();
    }

    public void Cancel()
    {
        this.backgroundWorker.CancelAsync();
    }

    private void DoTask()
    {
        this.taskToThread.Do();
    }

    private readonly ITask taskToThread;
    private readonly BackgroundWorker backgroundWorker;
}

Now, this could be combined with the TaskCancel and the TaskInterruptible so that a long-running task can be run on a separate thread until some interrupting event occurs, at which point some other task occurs in its stead.

Yeah? And? So? What?

The point to all of this is that, with these infrastructural implementations in place, we can happily contrive whatever flow of tasks we require –  without changing any of the clients that use this interface. This is bold, italic and underlined for a very good reason – it’s that important. We can achieve a separation of concerns and maintain the single responsibility principle, both at the same time. We separate the concerns by allowing a florid object graph to grow around an interface, rather than foisting that behavior on clients. We maintain single responsibility because each of the component parts are very much focused – you will have a class that provides a predicate*, a class that implements an interrupt, a handful of classes that implement tasks.

By exercising the interface segregation principle, these interfaces can spring up a lot. That isn’t to say that, for every interface, all of these implementations should be in place – far from it – merely that there are plenty of occasions where one or two of these used in collaboration can move complexity out of clients that have no business knowing about it.

If applied even minimally, there will be a lot of (good, laudable) constructor injection going on here. However, by leaning heavily on your dependency injection framework (Unity, Castle Windsor, Spring.NET, etc.), there is only a bit of up-front effort in registering the implementations in the application’s entry point. You can even write unit tests that will exercise the DI configuration by ensuring you can resolve all of the composition roots. If the graph can’t be constructed at initialization time, then you can implement a factory that can manually construct the graph depending on any kind of context you require.

We’re living off SOLIDs, now!

*Note that, in this example, I’m using a Func<bool> delegate as a predicate because I have no context that I wish to pass – something that Predicate<TContext> requires. If you wish, you could even define an interface IPredicate with a single bool method – called ‘Test’ or some such – because interfaces are much more flexible than delegates.

About these ads

4 responses to “I, Interface

  1. Jonathan Allen November 21, 2011 at 00:42

    Your ITask interface already exists in .NET. It is called Action and has one method named Invoke. And because it is a delegate interface instead of an abstract interface we get all kinds of syntax support.

    • garymcleanhall November 21, 2011 at 00:47

      While you are absolutely correct that you can just use an Action to represent a method like Do(), that really wasn’t the point of the post. The point is what you can do with any interface, not specifically this interface, which was chosen merely for simplicity and clarity. You can’t really decorate an Action in the way shown here.

  2. Dennis Somerville November 23, 2011 at 18:56

    Thanks for the post! This was an excellent article illustrating an extremely important topic.

    SOLID!

    Dennis

  3. Pingback: Ciekawe strony deweloperskie « Wiadomości o technologiach IT

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: