Growing with the Web

Delegation design pattern

Published , updated
Tags:

The delegation design pattern allows an object to delegate one or more tasks to a helper object. Two classes are used to achieve this; the delegate and delegator, both which realise a common interface. A method (or methods) on the interface represent the functionality to be delegated. A call to the delegator calls the matching function on the delegate.

While this seemingly just abstracts away some of the functionality into another class, the real power of this pattern comes when there are multiple delegates. The delegator typically has a method for each delegate that will convert the delegator to use that delegate.

It is useful for understanding to compare the delegation pattern to inheritance. Both are powerful reuse techniques with a few of key differences; inheritance is directly supported by today’s object-oriented programming languages and enables the use of polymorphism, whereas the delegation pattern allows the delegate to be changed at run-time.

Benefits

  • Clearly separates the different sets of functionality
  • Run-time flexibility

Drawbacks

  • Not as trivial as implementing inheritance

delegate keyword in C#

The delegate keyword may be useful for implementing the delegation pattern however the presence of the keyword does not mean C# supports the delegation design pattern. A delegate in C# is simply a type-safe function pointer.

UML diagram

Delegation UML diagram

Code examples

using System;

delegate void DelegateFunction();

class Delegator
{
    private DelegateFunction _function;

    public void F() { _function(); }

    public void ToA() { _function = A; }
    public void ToB() { _function = B; }

    private void A() 
    { 
        Console.WriteLine("Delegate A called");
    }
    
    private void B() 
    { 
        Console.WriteLine("Delegate B called");
    }
}
public class Delegator implements DelegationInterface {
    private DelegationInterface delegate;

    public String f() {
        if (delegate == null) {
            return null;
        }
        return delegate.f();
    }

    public void toA() { delegate = new DelegateA(); }
    public void toB() { delegate = new DelegateB(); }
}

interface DelegationInterface {
    String f();
}

public class DelegateA implements DelegationInterface {
    public String f() {
        return "Delegate A";
    }
}

public class DelegateB implements DelegationInterface {
    public String f() {
        return "Delegate B";
    }
}
// file: delegator.js

'use strict';

var DelegateA = require('./delegate-a.js');
var DelegateB = require('./delegate-b.js');

var Delegator = function () {
  this.delegate = undefined;
};

Delegator.prototype.f = function () {
  if (this.delegate) {
    return this.delegate();
  }
};

Delegator.prototype.toA = function () {
  this.delegate = DelegateA;
};

Delegator.prototype.toB = function () {
  this.delegate = DelegateB;
};

module.exports = Delegator;



// file: delegate-a.js

'use strict';

var DelegateA = function () {
  return 'a';
};

module.exports = DelegateA;



// file: delegate-b.js

'use strict';

var DelegateB = function () {
  return 'b';
};

module.exports = DelegateB;

Usage examples

There are countless examples that exist, anything where an object may behave in at least two different ways are ideal candidates.

  • An employee can be paid as full-time, part-time or casual
  • A document can be printed to a black and white printer, a colour printer or the screen
  • A game world can be displayed using a first-person view or a third-person view

Comments

comments powered by Disqus
Like this article?
Subscribe for more!