Jump to content

Delegation pattern

From Wikipedia, the free encyclopedia

This is an old revision of this page, as edited by 141.202.248.92 (talk) at 19:27, 15 June 2010 (→‎Complex Java example). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

In software engineering, the delegation pattern is a design pattern in object-oriented programming where an object, instead of performing one of its stated tasks, delegates that task to an associated helper object. It passes the buck, so to speak (technically, an Inversion of Responsibility). The helper object is called the delegate. The delegation pattern is one of the fundamental abstraction patterns that underlie other software patterns such as composition (also referred to as aggregation), mixins and aspects.

Examples

Simple Java example

In this Java example, the Printer class has a print method. This print method, rather than performing the print itself, delegates to class RealPrinter. To the outside world it appears that the Printer class is doing the print, but RealPrinter class is the one actually doing the work.

Delegation is simply passing a duty off to someone/something else. Here is a simple example:

 class RealPrinter { // the "delegate"
     void print() { 
       System.out.print("something"); 
     }
 }

 class Printer { // the "delegator"
     RealPrinter p = new RealPrinter(); // create the delegate 
     void print() { 
       p.print(); // delegation
     } 
 }
 
 public class Main {
     // to the outside world it looks like Printer actually prints.
     public static void main(String[] args) {
         Printer printer = new Printer();
         printer.print();
     }
 }

Complex Java example

By using interfaces, delegation can be made more flexible and typesafe. "Flexibility" here means, that C, need not refer to A or B in any way, the switching of delegation is abstracted from C. Needless to say, toA and toB don't count as references to A and B. 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()
     }
 }

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:

A: doing f()
A: doing g()
A: cleaning up.
B: doing f()
B: doing g()
B: cleaning up.

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.

Objective-C

// A custom view that scrolls its children.
@interface TCScrollView : NSView {

   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.
-(id)delegate;
-(void)setDelegate:(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

-(void)delegate {

   return _delegate;
}

-(void)setDelegate:(id)delegate {

   _delegate = delegate;
}

-(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 not, ignore the scroll request.
   if(!shouldScroll) 
      return;  
 
   /// 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

Objective-C 2.0

// A custom view that scrolls its children.
@interface TCScrollView : NSView {
 
   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
 
// This is a formal protocol: implementor doesn't have to implement all or even any of 
// the optional methods in the protocol
@protocol TCScrollViewDelegate 
 
@optional
-(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 not, ignore the scroll request.
   if(!shouldScroll) 
      return;  
 
   // Scrolling code omitted.
}
 
@end
 
@interface MyCoolAppController : NSObject <TCScrollViewDelegate> {
 
   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

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

Python

class I:
    def f(self): pass
    def g(self): pass
 
class A(I):
    def f(self):
        print "A: doing f()"
     
    def g(self):
        print "A: doing g()"
 
class B(I):
    def f(self):
        print "B: doing f()"
     
    def g(self):
        print "B: doing g()"

class C(I):
    def __init__(self):
        # delegation
        self.i = None
 
    def f(self):
        self.i.f()
        
    def g(self):
        self.i.g()
 
    # normal attributes
    def to_a(self):
        self.i = A()
        
    def to_b(self):
        self.i = B()
 
if __name__ == '__main__':
    c = C()
    c.to_a()
    c.f() # output: A: doing f()
    c.g() # output: A: doing g()
    c.to_b()
    c.f() # output: B: doing f()
    c.g() # output: B: doing g()

Tcl

#TclOO is part of Tcl in 8.6 addon package in 8.5  
if { [ catch {package require TclOO } err ] != 0 } {
    puts stderr "Unable to find package TclOO ... adjust your auto_path!";
}

oo::class create I {
    constructor {} {
    }
    method f { } {puts "Error please implement"; };
    method g { } {puts "Error please implement"; }; 
}
oo::class create A {
    superclass I
    constructor {} {
	next;
    }
    method f { } {
        puts "A : doing f()"
    }
    method g { } {
        puts "A : doing g()"
    }
}
oo::class create B {
    superclass I 
    constructor {} {
	next;
    }
    method f { } {
        puts "B : doing f()"
    }
    method g { } {
        puts "B : doing g()"
    }
}
oo::class create C {
    variable i
    constructor {} {
        # delegation
        set i [A new ]
    }
    method f { } {
        $i f
    }
 
    method g { } {
        $i g 
    } 
    # normal attributes
    method to_a { } {
        $i destroy;
	set i [A new ]
    }
    method to_b { } {
        $i destroy;
	set i [B new ]
    }
}
set c [C new ]
$c to_a
$c f   ; # output A : doing f
$c g   ; # output A : doing g
$c to_b
$c f   ; # output B : doing f
$c g   ; # output B : doing g

See also

- Delegation on rosettacode.org