CSC/ECE 517 Fall 2010/ch6 6a PC
Delegation-based programming languages
" I do not care how it is done, or who is it assigned to. If something is understood to be placed in the hands of another party, it is delegation " - 'Anonymous'
Introduction
Dictionary defines delegation as the assignment of authority and responsibility to another person to carry out specific activities.In programming context it is nothing different. It is basically entrusting an action to another agent. This term was initially introduced by Henry Lieberman in his 1986 paper "Using Prototypical Objects to Implement Shared Behavior in Object-Oriented Systems". [1] He defines delegation in object oriented languages as a programming language feature making use of the method lookup rules for dispatching so-called self-calls.
The above diagram depicts that a message receiver is asking another object (message holder) to do something on its behalf which may delegate it to someone and so on and so forth
Delegation
Delegation, is also referred to as aggregation,consultation, or forwarding. In delegation one class may contain an instance of another class, and delegate some responsibility to that class. This is also referred to as the has-a relationship. Aggregation should not be confused with composition. Both aggregation and composition are used to describe one object containing another object, but composition implies ownership [2]. Aggregation is more general and doesn't imply any responsibilities for memory management. A class which contains other classes is called a composite class, while a class being contained is called a composited or composed class [3].Delegation is a very powerful reuse technique. The provides run time flexibility. It is also called dynamic inheritance.[4]
Delegation can be implemented in many programming languages like Ada, Aikido, C, C#, C++, Common Lisp, D, E, Go, J, Java, JavaScript, Logtalk, Objective-C,Oz, Perl,Perl 6, PHP, PicoLisp, Pop11, Python, Ruby, TCL, Vorpal [5]
Example in a Java like language:
class Delegate { void doSomething() { // "this" is also known as "current", "me" and "self" in other languages this.callMe() } void callMe() { print("I am Delegate") } }
class Delegator { private Delegate d; // delegationlink public Delegator(Delegate d) { this.d = d; } void doSomething() { d.doSomething() // call doSomething() on the Delegate instance } void callMe() { print("I am Delegator") } } d = new Delegate() b = new Delegator(d) // establish delegation between two objects
Here calling b.doSomething() will result in "I am Delegate" being printed, since class Delegator "delegates" the method callMe() to a given object of class Delegate.
Delegation vs Inheritance
Inheritance:
- Inheritance is restricted to compile time
- Since it targets type rather than instances, it cannot be removed/changed at runtime
- Inheritance can be statically type-checked
Delegation:
- Delegation takes place at run-time
- Since it takes place at run-time, it can even be removed at run-time
- Cannot guarantee static type-safety (By using interfaces, delegation can be made more flexible and typesafe)
Hints to use Inheritance
- Inheritance is used to create sub-categories of objects
- Mostly identified in 'is-a' relationships between objects
- Inheritance is used when two objects are of the same type but one of the objects may need to have its own customized functionality. The child object just inherits from the parent and writes its own implementation of the feature it needs extra.
- Inheritance can be implemented on objects of the same type only.
Here we have an abstract base class. We extend this to to provide something that we can represent any product by. We then provide a few specialisations for typical products like book,cd.This is a usual case when inheritance is used.
Hints to use Delegation
- Delegation is used when two objects aren't of the same type but one has methods and attributes that the other wants to use internally instead of writing its own implementation
- Identified as 'as-a' relationships where the type of included object may change during runtime.
- Delegation is used when methods implementation needs to be handled by some other class within the parent tree. The class that will handle the method isn't known at compile time.
- Code execution within objects needs to be determined dynamically.
In the above diagram, class C inherits from A. Also, class C has a method called as Call which simple calls the method Call from class B. Hence, class C is delegating its functionality to be implemented by Class B. For this, class C has an object reference to class B.
Situations where you can use delegation
Consider following situation:
- Objective: We want a robot to have a heat sensor capability.
- Initial design: So we initially decide to build a Robot class like the one shown below:
- Problem with the design: Now all Robots should have a heat sensor. What if some robot did not have a heat sensor capability?
- Solution: So we make a Robot base class and a VolcanoRobot class which inherits from Robot and performs the heat-sensor operations
- Problem: But with this design whenever we want to modify anything related to the heat sensor, we will have to change the robot class. Also, with our design we have exposed heat sensor methods to Robot class.
- Solution: Hence, in such situations delegation is best.
- New design: Delegate the heat sensor functionality to Heat Sensor class. VolcanoRobot still has the 3 methods that are related to the sensor, but those are wrapper methods, they do nothing but to call the sensor corresponding ones, and that’s exactly what delegation is, just delegate functionality to the contained parts(delegates).
Delegation and composition go hand in hand to provide a flexible neat solution and also it serves the principle “separate changeable code from static one” . The price that one needs to pay for this is that we need wrapper methods, and extra time needed in processing because of the call of these wrapper methods.
Delegation Design Pattern
It is a design pattern in object oriented languages where an object expresses certain behavior to the outside but in reality delegates responsibility for implementing that behaviour to an associated object.
Consider the following examples in java:
class Subordinate // the "delegate" { void performTask() { System.out.print("Subordinate is performing"); } } class Manager // the "delegator" { Subordinate slave = new Subordinate(); // create the delegate void performTask() { slave.performTask(); // delegation } } public class Main { // to the outside world it looks like Manager is actually doing work. public static void main(String[] args) { Manager boss = new Manager(); boss.performTask(); } }
have to write more stuff here. Examples from various programming lang.
Delegation in Ruby
Delegation pattern in Ruby is implemented in 3 ways:
1)Forwardable lib:Forwardable lib is a library that supports delegation, it has 2 modules Forwardable and SingleForwardable:
Forwardable module
The Forwardable module provides delegation of specified methods to a designated object, using the methods def_delegator and def_delegators.
- def_delegator(obj, method, alias = method) : Defines a method method which delegates to obj. If alias is provided, it is used as the name for the delegate method.
- def_delegators(obj, *methods): Shortcut for defining multiple delegator methods, but with no provision for using a different name.
2)SingleForwardable module: The SingleForwardable module provides delegation of specified methods to a designated object, using the methods def_delegators. This module is similar to Forwardable, but it works on objects themselves, instead of their defining classes.
3)Delegate lib:Delegate Lib is another lib that provides delegation in Ruby
Example of delegation in Ruby
Using Forwardable module provided in Ruby, this is how we would implement Robot example seen above:
require 'forwardable' class Robot # Extending provides class methods extend Forwardable # Use of def_delegators def_delegators :@arm,:package,:stack # Use of def_delegator def_delegator :@heat_sensor, :measure ,:measure_heat def initialize @heat_sensor = HeatSensor.new @arm = RobotArm.new end end class HeatSensor #Celsius or Fahrenheit scale def measure(scale="c") t = rand(100) t = scale=="c" ? t : t * (9/5) puts "Heat is #{t}° #{scale.upcase}" end end class RobotArm def stack(boxes_number=1) puts "Stacking #{boxes_number} box(es)" end def package puts "Packaging" end end
Using Single Forwardable module: require "forwardable" require "date" Date = Date.today # output will be <Date: 4909665/2,0,2299161> # Prepare object for delegation date.extend SingleForwardable #=> #<Date: 4909665/2,0,2299161> # Add delegation for Time.now date.def_delegator :Time, "now","with_time" puts date.with_time #=>Thu Jan 01 23:03:04 +0200 2009
Using delegate lib: require "delegate" require "date" # Notice the class definition class CurrentDate < DelegateClass(Date) def initialize @date = Date.today # Pass the object to be delegated to the superclass. super(@date) end def to_s @date.strftime "%Y/%m/%d" end def with_time Time.now end end cdate = CurrentDate.new ## Notice how delegation works. Instead of doing cdate.date.day and defining attr_accessor for the date just do c.day puts cdate.day #=>1 puts cdate.month #=>1 puts cdate.year #=>2009 # Testing added methods like to_s puts cdate #=> 2009/01/01 puts cdate.with_time #=> Thu Jan 01 23:22:20 +0200 2009
Delegation in Java
Java supports delegation in the same way as other languages do. By using an instance of the class we would have otherwise inherited, and then forwarding messages to the instance we can do delegation in Java.[6] e.g. We can associate a class with a thread in two ways:
- By inheriting directly from class Thread.
- By implementing the Runnable interface and delegating to a Thread object.
Examples of Delegation in Java
Given below are the two examples of delegation, first being a simple one while the second being a complex example.
Simple Java Example
In this example, the class C has method which does not perform itself and rather delegates to class A, the methods f() and g(). It seems that Class C is doing the work but in reality class A is doing it.[7]
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 */ } }
void main() { C c = new C();
c.f(); c.g(); }
Complex Java Example
By using interfaces, delegation can be made more flexible[ C need not refer to classA or classB, delegation is abstracted] and typesafe. Here, class C can delegate to either class A or class B. The implements clauses, improves type safety, since this ensures that each of the class must implements the methods in the interface. The main tradeoff being more code.
interface I { void f(); void g(); }
class A implements I { void f() { system.out.println("A: doing f()"); } void g() { system.out.println("A: doing g()"); } }
class B implements I { void f() { system.out.println("B: doing f()"); } void g() { system.out.println("B: doing g()"); } }
class C implements I { // delegation I i = new A();
void f() { i.f(); } void g() { i.g(); }
// normal attributes void toA() { i = new A(); } void toB() { i = new B(); } }
void main() { C c = new C();
c.f(); c.g(); }
Table of Comparison
fjskdjfldsjl
Feature Matrix
kflksjfklsjd
Conclusion
Delegation is simply passing a duty off to someone/something else
References and Notes
- [1] Using prototypical objects to implement shared behavior in object-oriented systems
- [2] Aggregation
- [3] Object Composition
- [4] Issues Involved in Supporting Behavioral Evolution
- [5] Delegates
- [6] Darwin Project
- [7] Delegation in Ruby
- [8] Delegation Design Pattern
- [9] Examples of delegation in Java
- [10] [8]