Delegation pattern

From Wikipedia, the free encyclopedia

Jump to: navigation, search

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] 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();     // output: A: doing f()
         c.g();     // output: A: doing g()
         c.toB();
         c.f();     // output: B: doing f()
         c.g();     // output: B: doing 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() {
     // we use by default the instance of A.
     C c;
     // Here we are calling A methods.
     c.f();
     c.g();
     // Here delete the instance of A and switch with the instance of B.
     c.toB();
     // Now with the same methods we are calling B methods.
     c.f();
     c.g();
 
     // The delegate is deleted by normal C++ scoping rules.
 }

The output is :
A: doing f()
A: doing g()
A: cleaning up.
B: doing f()
B: doing g()
B: cleaning up.

[edit] Objective-C example

Delegation is very common in the Cocoa framework (the most common client library of Objective-C). Here's an example involving a scrolling view, which will ask its delegate if it's okay to scroll to a certain point before doing so.

 @interface TCScrollView : NSView// A custom view that scrolls its children.
 {
    id delegate; // A delegate that wants to act on events in this view
 }
 -(IBAction)scrollToCenter:(id)sender; // A method that can be bound to a button in the UI
 -(void)scrollToPoint:(NSPoint)to;
 
 // Accessors. Implementation not shown.
 @property (nonatomic, assign) id delegate;
 @end
 
 // A category on NSObject describing possible TCScrollView delegate methods.
 // This is an informal protocol: implementor doesn't have to implement all or even any of 
 // the methods in the protocol
 @interface NSObject (TCScrollViewDelegate) 
 -(BOOL)scrollView:(TCScrollView*)scrollView shouldScrollToPoint:(NSPoint)newPoint;
 @end
 
 @implementation TCScrollView
 -(IBAction)scrollToCenter:(id)sender; { 
   [self scrollToPoint:NSPointMake(0,0)];
 }
 -(void)scrollToPoint:(NSPoint)to;
 {
   BOOL shouldScroll = YES;
   // If we have a delegate, and that delegate indeed does implement our delegate method,
   if(delegate && [delegate respondsToSelector:@selector(scrollView:shouldScrollToPoint:)])
     shouldScroll = [delegate scrollView:self shouldScrollToPoint:to]; // ask it if it's okay to scroll to this point.
 
   if(!shouldScroll) return;  // If not, ignore the scroll request.
 
   /// Scrolling code omitted.
 }
 @end
 
 
 @interface MyCoolAppController
 {
    IBOutlet TCScrollView* scrollView;
 }
 @end
 @implementation MyCoolAppController
 -(void)awakeFromNib; {
   [scrollView setDelegate:self];
 }
 -(BOOL)scrollView:(TCScrollView*)scrollView shouldScrollToPoint:(NSPoint)newPoint;
 {
   if(newPoint.x > 0 && newPoint.y > 0)
     return YES;
   return NO;
 }
 @end

[edit] Example using PHP

Say you write a parent class to model cats and then extend the class to specific cats:

 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 Draw()
   {
     echo 'I look like a tabby' ;
   }
 }
 
 class BlackCat extends Cat
 {
   function Draw()
   {
     echo 'I have sleek black fur' ;
   }
 }

This code is fine; however, let's 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 class ICat
 {
   private $SoundBehavior ;
   private $SnuggleBehavior ;
   private $EatBehavior ;
   private $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] Complex Eiffel example

This example is a Eiffel version of the complex Java example above.

 deferred class I feature
     f is deferred end
     g is deferred end
 end
 
 class A inherit I feature
     f is do print("A: doing f%N") end
     g is do print("A: doing g%N") end
 end
 
 class B inherit I feature
     f is do print("B: doing f%N") end
     g is do print("B: doing g%N") end
 end
 
 class C inherit I creation to_a, to_b feature
     i: I
 
     f is do i.f end
     g is do i.g end
 
     to_a is do create {A} i end
     to_b is do create {B} i end
 end
 
 class MAIN creation main feature
     main is local c: C do
         create c.to_a
         c.f
         c.g
         c.to_b
         c.f
         c.g
     end
 end

[edit] Criticisms

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

[edit] See also

[edit] External Links

Personal tools