Delegation pattern
From Wikipedia, the free encyclopedia
|
|
This article contains too much jargon and may need simplification or further explanation. Please discuss this issue on the talk page, and/or remove or explain jargon terms used in the article. Editing help is available. (July 2009) |
In software engineering, the delegation pattern is a design pattern in object-orientated 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.
Contents |
[edit] Examples
[edit] 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.
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(); } }
[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:
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] 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] 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()
[edit] 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