Click here to Skip to main content
Click here to Skip to main content

Use of Interfaces and Delegates for an Extensible Design

, 1 Jun 2007
Rate this:
Please Sign up or sign in to vote.
This article uses a real life example to demonstrate the use of delegates and interfaces to make the design extensible.

Introduction

A good class design should be such that there should be a loose coupling between the different classes involved and the design must be extensible. A loose coupling between classes means that the interdependency between them should be minimal. Extensibility is the ease with which the design can take changes in functionality.

In this article, I have taken a simple real life problem and tried to solve it using three different approaches:

  1. A simple approach
  2. Interface based approach
  3. Delegate based approach

The Problem Statement

An employee is assigned a certain time consuming task. He is supposed to finish this task and notify his managers about the result.

A Simple Approach

This approach is a usual developer approach which aims to solve the problem at hand and not think about future enhancements. So as per the problem, we can identify two entities namely Manager and Employee. So let us have two classes Employee and Manager. Employee will have a method which we shall name as DoWork and Manager will have a method called Notify. The implementation is as given below:

Employee:: DoWork

public void DoWork(string workDesc, 
params Manager[] managers)
{
      Console.WriteLine("Doing work... - " + workDesc);

      //Some time consuming work

      Thread.Sleep(3000);

      //Notify the managers of the result

      foreach(Manager manager in managers)
             manager.Notify("Done with the work!!!");
}

Manager:: Notify

public void Notify(string status)
{
     Console.WriteLine("Manager {0} notified. 
Status of work: {1}",_name, status);
}

There is a tight coupling between the two classes in this approach. What would happen if the functionality changes. Let us say that along with managers, the employee also needs to notify the customers about the status of the work. We would then have a new class called Customer. The DoWork method would then need to take an array of customers and also notify them. This would require some rework of the Employee class.

Interface Based Approach

Taking extensibility into consideration, we change the design slightly by adding an interface called IBoss. IBoss declaration is as given below:

public interface IBoss
{
      void Notify(string status);
}

The Manager class and the newly added class called Customer would implement IBoss.

Let us change the DoWork method of the Employee and get rid of the parameter managers. So how do we notify all the bosses? For this, let us add an interface called IWorker with a declaration as given below:

public interface IWorker
{
    void Register(IBoss boss);
    void UnRegister(IBoss boss);
    void DoWork(string workDesc);
}

Employee will now implement IWorker as follows:

private ArrayList _bosses = new ArrayList();

public void Register(IBoss boss) //IWorker.Register
{
  _bosses.Add(boss);
}

public void UnRegister(IBoss boss) //IWorker.UnRegister
{
    _bosses.Remove(boss);
}

public void DoWork(string workDesc)
{
      Console.WriteLine("Doing work... - " + workDesc);

      //Some time consuming work
      Thread.Sleep(3000);

      //Employee need not care whom to notify.
      //All the registered bosses will be notified about
      //the status of the work. 
      for(int index = 0; index < _bosses.Count; index++)
      {
            IBoss boss = (IBoss)_bosses[index];
            boss.Notify("Done with the work !!!");
      }
}

This is an implementation of the Observer design pattern. Here the worker is an observable object and the bosses are the observers. This certainly seems to be a better approach as anyone who needs to get notified needs to implement the IBoss interface and register with a worker. Hence once implemented, there is absolutely no change required in the Employee class!!! Moreover the bosses are not tied to the Employee. They are free to register with any Worker.

Delegate Based Approach

Though the interface based approach seems to be the right solution, let us also try out implementing the same using delegates. Delegates and Interfaces are conceptually similar because they are both implementation contracts.

First of all, we need to declare a delegate.

public delegate void NotifierDelegate(string status);

The Register method of the Employee has to be changed to take this delegate as a parameter.

private NotifierDelegate _notifyDelegate;

public void Register
(NotifierDelegate notifyDelegate)
{
    _notifyDelegate = notifyDelegate;
}

Employee::DoWork would now look like this:

public void DoWork(string workDesc)
{
    Console.WriteLine("Doing work... : " + workDesc);

    //Some time consuming work
    Thread.Sleep(3000);
            
    //Employee need not care whom to notify 
    //and what methods to call. All interested parties 
    //will be notified about the status of the work.
    if(_notifyDelegate != null)
        _notifyDelegate("Done with the work !!!");
}

The Manager and Customer classes now need not implement from any interface. They just need to have methods that can be encapsulated by the NotifierDelegate.

NotifierDelegate manDelegate
= new NotifierDelegate(manager.Notify); 

NotifierDelegate custDelegate
= new NotifierDelegate(customer.SetStatus);

Let us now combine the delegates to form a multicast delegate. Note that since the NotifierDelegate has been declared with a void return type, this is possible.

NotifierDelegate combinedDelegate 
= manDelegate + custDelegate; 

All we then have to do is to register the multicast delegate with the employee and call DoWork.

Employee emp = new Employee();
emp.Register(combinedDelegate); 
emp.DoWork("Get the vegetables.");

This approach is very similar to the interface approach. But here the classes are not bound to implement any interface. They only need to have methods which match the delegate signature. The employee also need not know whom to notify and which methods to call. It just has a set of delegates which it needs to invoke. Hence this removes the tight coupling between the two classes.

Conclusion

I hope this article has given the readers an insight into how to make the class design more manageable and extensible. I also believe that it has demonstrated the application of delegates in a design and the similarities between interfaces and delegates.

History

  • 1st June, 2007: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Manjesh Shetty H
Web Developer
India India
No Biography provided

Comments and Discussions

 
QuestionLocation of combined delegate PinmemberDeepak Kolapkar9-Sep-07 1:48 
QuestionObserver Pattern? Pinmembernarsyseth1-Jun-07 6:35 
AnswerRe: Observer Pattern? Pinmembereasiaccess1-Jun-07 7:14 
AnswerRe: Observer Pattern? Pinmemberclawlor1-Jun-07 8:34 
GeneralRe: Observer Pattern? Pinmembernarsyseth1-Jun-07 11:18 
GeneralRe: Observer Pattern? PinmemberManjesh Shetty H3-Jun-07 21:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140721.1 | Last Updated 1 Jun 2007
Article Copyright 2007 by Manjesh Shetty H
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid