Delegation pattern

From Wikipedia, the free encyclopedia

In software engineering, the delegation pattern is a technique where an object outwardly expresses certain behaviour but in reality delegates responsibility for implementing that behavior to an associated object in an Inversion of Responsibility. The delegation pattern is the fundamental abstraction that underpins composition (also referred to as aggregation), mixins and aspects.

Contents

[edit] Applicability Example using PHP

Say you write a parent class to model cats, then extend the class to specific cats as follows:

class Cat {
  function Meow()
  {
    echo 'Meow' ;
  }
  function Snuggle()
  {
    echo 'Purr' ;
  }
  function Eat()
  {
    $this->meow() ;
    $this->meow() ;
    $this->meow() ;
    echo 'I begged until I got food, now I am eating' ;
  }
  function Sleep()
  {
     echo 'Curling up on cat bed' ;
  }
}

class Tabby extends Cat
{
  function Show()
  {
    echo 'I look like a tabby' ;
  }
}

class BlackCat extends Cat
{
  function Show()
  {
    echo 'I have sleek black fur' ;
  }
}

This code is fine and dandy, however, lets say you watch a documentary about lions and realize you didn't model lions, at the same time you also think you would like to add in cheetahs and tigers, so you set about implementing the following Lion class.

class Lion extends Cat
{
  function Meow()
  {
    echo 'I can't meow' ;
  }
  function Roar()
  {
    echo 'roar' ;
  }
  function Snuggle()
  {
    echo 'Eat the fool who tried to snuggle with me' ;
  }
  function Sleep()
  {
    echo 'Curling up under tree';
  }
  function Eat()
  {
    echo 'Hunt gazelles' ;
  }
  function Draw()
  {
    echo 'I look like a lion' ;
  }
}

So now you have a lion class, however you also have a new roar() method. Now you have to edit all your code to test if this is a lion, and if so call roar instead of meow. You realize that tigers also roar, sleep under trees and don't snuggle well, but they don't eat gazelles. Perhaps you could make a big cat class and extend it to tigers and lions and override the base classes. But then you realize a cheetah is a big cat that sleeps under trees and eat gazelles, but they don't roar. This is getting messier by the minute. Next you see another documentary with civet cats and they eat birds and sleep in the trees and make a different sound. Oh no! Your code is now a disaster filled with classes extended left and right with all kinds of methods being overridden. On top of all this you also realize that if you end up with hundreds of cats who sleep under trees that override the sleep method and want to change what that method does, you will need to edit every single place that is overridden.

Let's try this a little differently and define an abstract cat interface as follows:

abstract ICat
{
  var $SoundBehavior ;
  var $SnuggleBehavour ;
  var $EatBehavior ;
  var $SleepBehavior ;

  function MakeSound()
  {
    $this->SoundBehavior->MakeSound() ;
  }
  function Snuggle()
  {
    $this->SnuggleBehavior->Snuggle() ;
  }
  function Eat()
  {
    $this->EatBehavior->Eat() ;
  }
  function Sleep()
  {
    $this->SleepBehavior->Sleep() ;
  }
  function Draw(){}
  function setSoundBehavior( $SoundBehavior )
  {
    $this->SoundBehavior = $SoundBehavior ;
  }
  function setSnuggleBehavior( $SnuggleBehavior )
  {
    $this->SnuggleBehavior = $SnuggleBehavior ;
  }    
  function setEatBehavior( $EatBehavior )
  {
    $this->EatBehavior = $EatBehavior ;
  }
  function setSleepBehavior( $SleepBehavior )
  {
    $this->SleepBehavior = $SleepBehavior ;
  }
}

Now create an interface for each behavior, then an implementation using SoundBehavior as an example (each behavior would need its own interface/implementation):

interface SoundBehavior
{
  function MakeSound(){}
}
class Roar implements SoundBehavior
{
  function MakeSound()
  {
    echo 'Roar' ;
  }
}
class Meow implements SoundBehavior
{
  function MakeSound()
  {
    echo 'Meow' ;
  }
}

Assuming you created all your behaviours, let's define a lion and a cat

class HouseCat extends ICat
{
  function Cat()
  {
    $this->SetSoundBehavior( new Meow() ) ;
    $this->SetSnuggleBehavior( new PurrSnuggle() ) ;
    $this->SetEatBehavior( new BegForFood() ) ;
    $this->SetSleepBehavior( new SleepInBed() ) ;
  }
  function Draw()
  {
    echo 'I look like a plain cat' ;
  }
}

class Tabby Extends HouseCat
{
  function Draw()
  {
    echo 'I look like a Tabby' ;
  }
}

class Tiger extends ICat
{
  function Cat()
  {
    $this->SetSoundBehavior( new Roar() ) ;
    $this->SetSnuggleBehavior( new EatSnuggler() ) ;
    $this->SetEatBehavior( new HuntGazelles() ) ;
    $this->SetSleepBehavior( new SleepUnderTree() ) ;
  }
  function Draw()
  {
    echo 'I look like a tiger' ;
  }
}

This final abstract interface ICat, delegates responsibility of cat behavior instead of using methods in a base class and overriding them where needed. If we didn't use delegation above we would need to override base method classes repeatedly in subclasses. Additional classes can be added which share some, but not all, of the cat functionality using a different set of delegates.

[edit] Examples

[edit] Simple Java example

In this Java programming language example, the class C has method stubs that forward the methods f() and g() to class A. Class C pretends that it has attributes of class A.

class A {
    void f() { System.out.println("A: doing f()"); }
    void g() { System.out.println("A: doing g()"); }
}

class C {
    // delegation
    A a = new A();

    void f() { a.f(); }
    void g() { a.g(); }

    // normal attributes
    X x = new X();
    void y() { /* do stuff */ }
}

public class Main {
    public static void main(String[] args) {
        C c = new C();
        c.f();
        c.g();
    }
}

[edit] Complex Java example

By using interfaces, delegation can be made more flexible and typesafe. In this example, class C can delegate to either class A or class B. Class C has methods to switch between classes A and B. Including the implements clauses improves type safety, because each class must implement the methods in the interface. The main tradeoff is more code.

interface I {
    void f();
    void g();
}

class A implements I {
    public void f() { System.out.println("A: doing f()"); }
    public void g() { System.out.println("A: doing g()"); }
}

class B implements I {
    public void f() { System.out.println("B: doing f()"); }
    public void g() { System.out.println("B: doing g()"); }
}

class C implements I {
    // delegation
    I i = new A();

    public void f() { i.f(); }
    public void g() { i.g(); }

    // normal attributes
    void toA() { i = new A(); }
    void toB() { i = new B(); }
}


public class Main {
    public static void main(String[] args) {
        C c = new C();
        c.f();
        c.g();
        c.toB();
        c.f();
        c.g();
    }
}

[edit] Complex C++ example

This example is a C++ version of the complex Java example above. Since C++ does not have an interface construct, a pure virtual class plays the same role. The advantages and disadvantages are largely the same as in the Java example.

#include <iostream>
using namespace std;

class I {
  public:
    virtual void f() = 0;
    virtual void g() = 0;
    virtual ~I() {}
};

class A : public I {
  public:
    void f() { cout << "A: doing f()" << endl; }
    void g() { cout << "A: doing g()" << endl; }
    ~A() { cout << "A: cleaning up." << endl; }
};

class B : public I {
  public:
    void f() { cout << "B: doing f()" << endl; }
    void g() { cout << "B: doing g()" << endl; }
    ~B() { cout << "B: cleaning up." << endl; }
};

class C : public I {
  public:
    // construction/destruction
    C() : i( new A() ) { }
    virtual ~C() { delete i; }

  private:
    // delegation
    I* i;

  public:
    void f() { i->f(); }
    void g() { i->g(); }

    // normal attributes
    void toA() { delete i; i = new A(); }
    void toB() { delete i; i = new B(); }
};

int main() {
    C* c = new C();

    c->f();
    c->g();
    c->toB();
    c->f();
    c->g();
}

[edit] Criticisms

This pattern typically sacrifices speed optimization in favor of enhanced clarity of abstraction.

[edit] See also

In other languages