- Delegation pattern
-
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. There is an Inversion of Responsibility in which a helper object, known as a delegate, is given the responsibility to execute a task for the delegator. 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.
Contents
Examples
Java examples
Simple
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
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, as 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 public void toA() { i = new A(); } public 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() } }
C# example
This is a C# example of the complex Java example above.
public interface I { void F(); void G(); } public class A : I { public void F() { System.Console.WriteLine("A: doing F()"); } public void G() { System.Console.WriteLine("A: doing G()"); } } public class B : I { public void F() { System.Console.WriteLine("B: doing F()"); } public void G() { System.Console.WriteLine("B: doing G()"); } } public class C : I { // delegation I i = new A(); public void F() { i.F(); } public void G() { i.G(); } // normal attributes public void ToA() { i = new A(); } public void ToB() { i = new B(); } } public class Program { public static void Main() { 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() } }
C++ example (complex)
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.
Eiffel example (complex)
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
Objective-C example
Delegation is very common in the Cocoa framework. The following example is an Objective-C version of the complex Java example above.
@protocol I <NSObject> -(void) f; -(void) g; @end @interface A : NSObject <I> { } @end @implementation A -(void) f { NSLog(@"A: doing f"); } -(void) g { NSLog(@"A: doing g"); } @end @interface B : NSObject <I> { } @end @implementation B -(void) f { NSLog(@"B: doing f"); } -(void) g { NSLog(@"B: doing g"); } @end @interface C : NSObject <I> { id<I> i; // delegation } -(void) toA; -(void) toB; @end @implementation C -(void) f { [i f]; } -(void) g { [i g]; } -(void) toA { [i release]; i = [[A alloc] init]; } -(void) toB { [i release]; i = [[B alloc] init]; } // constructor -(id)init { if (self = [super init]) { i = [[A alloc] init]; } return self; } // destructor -(void)dealloc { [i release]; [super dealloc]; } @end int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; C *c = [[C alloc] init]; [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 [c release]; [pool drain]; return 0; }
Perl
use strict; use warnings; use 5.010_000; package Example::Printer::Delegate::Role; use Moose::Role; =for comment Roles are like interfaces in other languages but are more powerful. =cut requires 'print_string'; # 'print_string' is required. =for optional_methods greet =cut package Example::Printer::Delegate::A; use Moose; with 'Example::Printer::Delegate::Role'; sub print_string { say 'Printed from A'; } package Example::Printer::Delegate::B; use Moose; with 'Example::Printer::Delegate::Role'; sub print_string { say 'Printed from B'; } sub greet { say 'And a hello to you!'; } package Example::Printer; use Moose; has delegate => ( is => 'rw', does => 'Example::Printer::Delegate::Role', predicate => 'hasDelegate' ); sub print { my $self = shift; # Check to see, if I have a delegate if ( $self->hasDelegate ) { my $delegate = $self->delegate; # Call the delegate's print method. The role assures me that # print_string exists. $delegate->print_string; # Let's check to see if the delegate supports the greet method. if ( $delegate->can('greet') ) { # It does so, let's also greet people! $delegate->greet(); } } else { warn 'No delegate!'; } } package main; use strict; my $delegateA = Example::Printer::Delegate::A->new(); my $delegateB = Example::Printer::Delegate::B->new(); my $printer = Example::Printer->new(); $printer->print(); # Will get a warning about not having a delegate. say 'Setting delegate to Delegate A'; $printer->delegate($delegateA); $printer->print(); # This will call $a's print_string method. say 'Setting delegate to Delegate B'; $printer->delegate($delegateB); $printer->print(); # This will call $b's print_string, and greet methods.
Python Example (complex)
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 = A() 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.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()
Ruby on Rails example (complex)
module I def f puts "#{self.class}: doing f()" end def g puts "#{self.class}: doing g()" end end class A include I end class B include I end class C attr_accessor :i delegate :f, :g, :to => :i def initialize(klass = A) self.i = klass.new end end c = C.new c.f # output: A: doing f() c.g # output: A: doing g() c = C.new(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
PHP
<?php // Interface for printers. interface iPrinter { // 'print' is a reserved word. public function printIt($subject); } // LinePrinter Printer Delegate class LinePrinter implements iPrinter { public function printIt($line) { print $line; print "\n"; } } // ArrayPrinter Printer Delegate class ArrayPrinter implements iPrinter { public function printIt($array) { if ( ! is_array($array)) { throw new Exception ('Expected Array()'); } print_r($array); } } // Custom object. class MyObject { protected $printer; public function __construct() { // Set delegate. $this->printer = new LinePrinter; } public function printData() { $this->printer->printIt('Some string data'); } } // Another custom object class MyOtherObject { protected $printer; public function __construct() { // Set delegate. $this->printer = new ArrayPrinter; } public function printData() { $this->printer->printIt(array(1,2,3)); } } // ... // Run it. $object = new MyObject; $object->printData(); // Run it. $object = new MyOtherObject; $object->printData();
See also
- Delegation (programming)
- Design pattern
- Post-object programming
External links
- Delegation on Rosetta Code
Categories:- Articles with example C++ code
- Articles with example Java code
- Software design patterns
Wikimedia Foundation. 2010.