CSC/ECE 517 Fall 2010/ch3 S30 RJ: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
 
(26 intermediate revisions by the same user not shown)
Line 3: Line 3:
Most OOP overviews explain what the concept of Inheritance is in Object Oriented Programming, and how Inheritance works, but not many OOP overviews discuss alternatives when Inheritance may not be the best design choice for an OO application. This topic briefly introduces the OOP concepts of Inheritance, Decomposition, Message Forwarding, and Delegation, and then discusses when the concepts could be considered as potential alternatives to the use of Inheritance for some OOP applications.  
Most OOP overviews explain what the concept of Inheritance is in Object Oriented Programming, and how Inheritance works, but not many OOP overviews discuss alternatives when Inheritance may not be the best design choice for an OO application. This topic briefly introduces the OOP concepts of Inheritance, Decomposition, Message Forwarding, and Delegation, and then discusses when the concepts could be considered as potential alternatives to the use of Inheritance for some OOP applications.  


Beginning OOP developers often feel compelled to use Inheritance aggressively to achieve the advertized benefit of code re-use for their applications.  However the full life cycle of an OO application needs to be considered.  Developers usually agree more coding time will be spent maintaining and enhancing an OO application after it is developed, compared to the coding time needed to initially develop the application.  In this context, it is often better to use Decomposition, Message Forwarding, and Delegation techniques instead of inheritance techniques for an application. These techniques can provide more flexibility in the maintainence and enhancement aspects of an OO application.
Beginning OOP developers often feel compelled to use Inheritance aggressively to achieve the advertised benefit of code re-use for their applications.  However the full life cycle of an OO application needs to be considered.  Developers usually agree more coding time will be spent maintaining and enhancing an OO application after it is developed, compared to the coding time needed to initially develop the application.  In this context, it is often better to use Decomposition, Message Forwarding, and Delegation techniques instead of inheritance techniques for an application. These techniques can provide more flexibility in the maintenance and enhancement aspects of an OO application.


It needs to be noted that different OOP authors interchange and blur the meaning of what is meant by Decomposition, Message Forwarding, and Delegation in OOP.  In general, discussions about Decomposition, Message Forwarding, and Delegation in the context of OOP are suggesting alternative ways to accomplish OOP designs other than utilizing a traditional OOP design that uses pure Inheritance principles.
It needs to be noted that different OOP authors interchange and blur the meaning of what is meant by Decomposition, Message Forwarding, and Delegation in OOP.  In general, discussions about Decomposition, Message Forwarding, and Delegation in the context of OOP are suggesting alternative ways to accomplish OOP designs other than utilizing a traditional OOP design that uses pure Inheritance principles.
Line 9: Line 9:
== '''Inheritance in OOP Design''' ==
== '''Inheritance in OOP Design''' ==


Inheritance is one of the key concepts in Object Oriented Programming.  In OOP, parent classes describe and define attributes and capabilities of objects in a parent class. Subsequently subclasses can be defined to create objects that inherit or acquire attributes and capabilities from the parent class.  Parent classes are also known as the Superclass of the child, or subclass.  Programming code that is created to describe the attributes and methods of objects in a parent class can easily be reused by objects that are created in subclasses of the parent class. Using inheritance in a OO application is appropriate when the relationship between the Superclass and subclass is a 'Is-A' relationship. Consider the concept
Inheritance is one of the key concepts in Object Oriented Programming.  In OOP, parent classes describe and define attributes and capabilities of objects in a parent class. Subsequently subclasses can be defined to create objects that inherit or acquire attributes and capabilities from the parent class.  Parent classes are also known as the Superclass of the child, or subclass.  Programming code that is created to describe the attributes and methods of objects in a parent class can easily be reused by objects that are created in subclasses of the parent class. Using inheritance in a OO application is appropriate when the relationship between the Superclass and subclass is a 'Is-A' relationship.  
of [http://www.javaworld.com:80/javaworld/jw-11-1998/jw-11-techniques.html single inheritance through class extension][http://www.javaworld.com:80/javaworld/jw-11-1998/jw-11-techniques.html [1]]
 
=== Inheritance Example ===
Consider the concept of [http://www.javaworld.com:80/javaworld/jw-11-1998/jw-11-techniques.html single inheritance through class extension] [http://www.javaworld.com:80/javaworld/jw-11-1998/jw-11-techniques.html [1]]


<pre>
<pre>
Line 32: Line 34:


== '''Composition in OOP Design''' ==
== '''Composition in OOP Design''' ==
In OOP literature, discussions that mention object oriented decomposition often also discuss the topic of object oriented composition.  An alternative to an inheritance relationship is a [http://www.javaworld.com:80/javaworld/jw-11-1998/jw-11-techniques.html composition relationship][3], whereby instance variables of one object refer to other objects.  
In OOP literature, discussions that mention object oriented decomposition often also discuss the topic of object oriented composition.  An alternative to an inheritance relationship is a [http://www.javaworld.com:80/javaworld/jw-11-1998/jw-11-techniques.html composition relationship][http://www.javaworld.com:80/javaworld/jw-11-1998/jw-11-techniques.html [3]], whereby instance variables of one object refer to other objects.  
 
=== Composition Example ===


<pre>
<pre>
Line 49: Line 53:
[[Image:oop-pic2.jpg|frame|center|Figure 2. The composition relationship]]
[[Image:oop-pic2.jpg|frame|center|Figure 2. The composition relationship]]


The author of the article used as the basis for my material [http://www.javaworld.com:80/javaworld/jw-11-1998/jw-11-techniques.html composition relationship][4] states the following: "In this example, Apple is what I will call the front-end class and Fruit is what I will call the back-end class. In a composition relationship, the front-end class holds a reference in one of its instance variables to a back-end class"
In this example, Apple can be called the front-end class, and Fruit is called the back-end class. In a composition relationship, the front-end class holds a reference in one of its instance variables to a back-end class. With inheritance relationships, it is usually easy to add new subclasses, however changes made to the interface of the Superclass in an inheritance relationship are often said to be 'fragile', because such changes may cause problems for subclasses in the relationship, and at times cause 'rippled' problems for all subclasses of the superclass.


With inheritance relationships, it is usually easy to add new subclasses, however changes made
How do composition and inheritance compare with each other?
to the interface of the Superclass in an inheritance relationship are often said to be 'fragile', because such changes may cause problems for subclasses in the relationship, and at times cause 'rippled' problems for all subclasses of the superclass.


The author of the article poses the question "So how exactly do composition and inheritance compare?", and then the author provides his thoughs on several points of comparison:
* It is easier to change the interface of a back-end class (composition) than a superclass (inheritance). As the previous example illustrated, a change to the interface of a back-end class necessitates a change to the front-end class implementation, but not necessarily the front-end interface. Code that depends only on the front-end interface still works, so long as the front-end interface remains the same. By contrast, a change to a superclass's interface can not only ripple down the inheritance hierarchy to subclasses, but can also ripple out to code that uses just the subclass's interface.


* It is easier to change the interface of a back-end class (composition) than a superclass (inheritance). As the previous example illustrated, a change to the interface of a back-end class necessitates a change to the front-endclass implementation, but not necessarily the front-end interface. Code that depends only on the front-end interface still works, so long as the front-end interface remains the same. By contrast, a change to a superclass's interface can not only ripple down the inheritance hierarchy to subclasses, but can also ripple out to code that uses just the subclass's interface.
* It is easier to change the interface of a front-end class (composition) than a subclass (inheritance). Just as superclasses can be fragile, subclasses can be rigid. You can't just change a subclass's interface without making sure the subclass's new interface is compatible with that of its supertypes. For example, you can't add to a subclass a method with the same signature but a different return type as a method inherited from a superclass. Composition, on the other hand, allows you to change the interface of a front-end class without affecting backend classes.
 
* It is easier to change the interface of a front-end class (composition) than a subclass (inheritance). Just as`superclasses can be fragile, subclasses can be rigid. You can't just change a subclass's interface without making`sure the subclass's new interface is compatible with that of its supertypes. For example, you can't add to a`subclass a method with the same signature but a different return type as a method inherited from a superclass. Composition, on the other hand, allows you to change the interface of a front-end class without affecting backend classes.


* Composition allows you to delay the creation of back-end objects until (and unless) they are needed, as well as changing the back-end objects dynamically throughout the lifetime of the front-end object. With inheritance, you get the image of the superclass in your subclass object image as soon as the subclass is created, and it remains part of the subclass object throughout the lifetime of the subclass.
* Composition allows you to delay the creation of back-end objects until (and unless) they are needed, as well as changing the back-end objects dynamically throughout the lifetime of the front-end object. With inheritance, you get the image of the superclass in your subclass object image as soon as the subclass is created, and it remains part of the subclass object throughout the lifetime of the subclass.
* It is easier to add new subclasses (inheritance) than it is to add new front-end classes (composition), because inheritance comes with polymorphism. If you have a bit of code that relies only on a superclass interface, that code can work with a new subclass without change. This is not true of composition, unless you use composition with interfaces. Used together, composition and interfaces make a very powerful design tool.
* The explicit method-invocation forwarding (or delegation) approach of composition will often have a performance cost as compared to inheritance's single invocation of an inherited superclass method implementation.
* With both composition and inheritance, changing the implementation (not the interface) of any class is easy. The ripple effect of implementation changes remain inside the same class.


== '''Message Forwarding in OOP Design''' ==
== '''Message Forwarding in OOP Design''' ==


Message forwarding between objects, as implemented in dynamic typing OO languages such as Objective-C, provides an alternative to inheritance relationships between objects that are statically determined at compile time.
Message forwarding between objects, as implemented in dynamic typing OO languages such as Objective-C, provides an alternative to inheritance relationships between objects that are statically determined at compile time. Using message sending, an object can always forward a message to another object in case it does not know how to handle the message.  Alternately, an
 
object can forward a message to another object intentionally, based on some criteria within the message. In OOP message forwarding incorporates concepts that are used to describe delegation (see next section) and composition.  
In an article entitled [http://macdevcenter.com/lpt/a/3642 "Objective-C Dynamite!"][5], author
Andrew Duncan describes message fowarding as follows:
 
*''....(the static compile time nature of Inheritance relationships.... is an unwelcome intrusion of implementation details into the conceptual design and practical use of a language. What's so bad about sending a message that is not understood? If a person on the street asks you for a flump, and you don't know how to respond, do you exit with a core dump? Do you feel that a Higher Power should prohibit the question from even being asked?''
 
*''Objective-C takes a simpler approach to method calls: it maintains runtime information about the class hierarchy, and searches it at call time for the appropriate code to handle each method call. The speed of modern processors, hardware tricks like branch prediction and speculative execution, and Objective-C runtime tricks like caching of search results, make this method-call implementation fast enough for all but the most nested of loops. In addition, this procedure can tell when the receiver has no matching method, and so can handle this situation in a defined way. Objective-C provides default behavior for unhandled messages but lets you customize it at will.''


*''Objective-C refers to this approach to calling methods as '''sending messages to objects''', which describes well this outlook: inter-object communication is not like a function call, but more like mailing a letter, which gets delivered by a mechanism that is slower but more flexible than just branching to a new execution address. This makes possible all the techniques mentioned in the bullet points above. In addition, distributed programs are easier to design and implement, because message sending does not depend on one distributed component's knowing the inheritance hierarchy of another.''
=== Message Forwarding Example ===


[http://developer.apple.com/legacy/mac/library/documentation/Cocoa/Conceptual/OOPandObjC1/OOPandObjC1.pdf Figure 3] below is a depiction of message forwarding, as implemented by the language Objective-C.
[http://developer.apple.com/legacy/mac/library/documentation/Cocoa/Conceptual/OOPandObjC1/OOPandObjC1.pdf Figure 3] below is a depiction of message forwarding, as implemented by the language Objective-C.
Line 85: Line 74:
[[Image:oop-pic3.jpg|frame|center|Figure 3. Message Forward in the language Objective-C]]
[[Image:oop-pic3.jpg|frame|center|Figure 3. Message Forward in the language Objective-C]]


''In this illustration, an instance of the Warrior class forwards a negotiate message to an instance of  the Diplomat class. The Warrior will appear to negotiate like a Diplomat. It will seem to respond to the negotiate message, and for all practical purposes it does respond (although it’s really a Diplomat that’s doing the work.'' [http://developer.apple.com/legacy/mac/library/documentation/Cocoa/Conceptual/OOPandObjC1/OOPandObjC1.pdf [6]]
In this illustration, an object sends a negotiate type of message to the Warrior object, expecting the Warrior object to fully handle and manage this negotiate message request.  However, unknown to the original object sender, when the Warrior object receives this message, the Warrior object chooses to forward the negotiate message on to an object from the Diplomat class. The Diplomat object processes the negotiate message, and sends it's response back to the Warrior object, which in turn returns the message response back to the original sender.  In this illustration the Warrior object explicitly recognized the negotiate message should be sent to the Diplomat object to be processed. Other OOP message forwarding techniques, such as implemented in Objective-C, allow objects to forward any 'unrecognized' messages they have received to objects that specifically exist to process unexpected messages. [http://developer.apple.com/legacy/mac/library/documentation/Cocoa/Conceptual/OOPandObjC1/OOPandObjC1.pdf [4]]
 
The author of the main article [http://macdevcenter.com/lpt/a/3642 "Objective-C Dynamite!" [5]], referenced in this section on message forwarding offers the following reasons for considering message forwarding instead of statically determined inheritance:


*''During development, a lot of design issues are in flux. It's a lot easier to work out how your framework is supposed to behave without having to start out in a most-factored state, with plenty of empty interface fluff. In fact, Objective-C lets you use static typing as well, so once you settle on a design,
Message forwarding in a dynamic language like Objective-C can provide better design time flexibility and run time flexibility to applications versus a traditional inheritance design and implementation.  During the development of an OO application, many design issues are not yet fully determined. It's a lot easier to work out how your framework is supposed to behave without having to start out in a most-factored state, with plenty of empty interface fluff. A dynamic language like Objective-C lets you use static typing as well, so once you settle on a design, you can declare your types with no penalty. Best of both worlds. [http://macdevcenter.com/lpt/a/3642 "Objective-C Dynamite!" [5]]
you can declare your types with no penalty. Best of both worlds.''
 
*''Objective-C objects are more flexible because of this tolerance for unrecognized messages. In effect, they are already implementers (in an empty sense) of an infinite number of as-yet-undesigned interfaces. You can plug them into all sorts of designs, and if they have anything to offer, any service to provide, they will provide it; otherwise they will just get out of the way.''
 
*''Program safety is not decidable anyway. In any nontrivial program there are many bugs, among which type errors are a small subset; and an unhandled message may not even be a bug. And in any case, Objective-C provides a mechanism for intercepting and either discarding or redirecting unhandled messages. If it really
is a bug, wouldn't you prefer logging an error to calling some unexpected (and definitely wrong) method, as you would do in C++ if you called a method on an object that doesn't have it? (You would have to use casting to do this, to get around the compiler's static type-checking, of course.)''


== '''Delegation in OOP Design''' ==
== '''Delegation in OOP Design''' ==
Line 101: Line 82:
Delegation is defined as: The notion that an object can issue a message to another object in
Delegation is defined as: The notion that an object can issue a message to another object in
response to a message. The first object therefore delegates the responsibility to the second
response to a message. The first object therefore delegates the responsibility to the second
object. [8] The Gang of Four discuss delegation in their introduction to the Design Patterns book. [9] ''Delegation is a way of making composition as powerful for reuse as inheritance.''
object [6]The Gang of Four discuss delegation in their introduction to the Design Patterns book [7]. ''Delegation is a way of making composition as powerful for reuse as inheritance.''
 
=== Basics of delegation ===
Two objects are involved in delegation. There is the object that receives a message, and the object that it delegates the responsibility to. It can be shown in a UML class diagram as the following:


[[Image:oop-pic4.jpg|frame|center|Figure 4. Delegation in OOP]]
[[Image:oop-pic4.jpg|frame|center|Figure 4. Delegation in OOP]]


  Delegation in OOP strives to allow one object to reuse functionality
=== Relation to inheritance ===
that another object can readily provide.  Delegation in OOP can therefore be said to realize the benefits
Delegation is often used instead of inheritance to produce robust designs. With inheritance, a subclass will often "delegate" the response to a specific message to the parent class as shown in the following diagram:
of code reuse, but not in an official OOP code reuse manner that is usually implied as a reason
for implementing traditional OOP inheritance. Instead of creating new code in a parent or Superclass that
can then be reused by a child or subclass object, delegation invokes services from other objects, thereby
taking advantage of code that already exists to support the other existing object. The functionality available in the use of mixins in Ruby on Rails would be an example of delegation in OOP.


One OO [http://developergeeks.com/article/16/when-not-to-use-inheritance-but-delegation developer][7] offers the following thoughts about '''When NOT to use Inheritance but Delegation''':
[[Image:oop-pic5.jpg|frame|center|Figure 5. Inheritance and Delegation in OOP]]


*''As we know that Inheritance is a good mechanism for re-using and extending the behavior of a class, but it should not seen as the only way to achieve this. It is not always right to use Inheritance to achieve re-usability and extensibility in architecture. Delegation provides an excellent alternative way for extending the behavior of a class or system. There are several situations when Delegation is more useful and flexible compared to Inheritance. For example:''
In this case, when the doSomething() method is invoked upon the Child object, it delegates the
handling of the message to its parent.


*''If an object can play one or more roles in system, then sub-classing becomes impractical, as the number of combinations may grow exponentially. In this case, Delegation would result in fewer classes.''
=== Static vs. Dynamic delegation ===
Inheritance represents a static relationship between classes. If you have child Class S that is a subclass of a parent Class P, you cannot change that relationship. Any instance of S will inherit the behaviors and members of P. Once the relationship is defined, you cannot change it. Clearly, the subclass can override the behavior, but again, this is static. The actual method that will handle the message is known at compile time.


*''If you find that an object needs to be a different subclass of a given class at different points of time in your application, it is advisable to use Delegation instead of Inheritance. Inheritance is a static relationship. If a class inherits from another class, this relationship cannot be changed dynamically. On the other hand, Delegation is a dynamic relationship that can be changed at runtime.''
Delegation, on the other hand, is a dynamic relationship. You can change the object that is the target of the delegation. That is, the receiver of the message can decide who should handle the message. Delegation in OOP strives to allow one object to reuse functionality that another object can readily provide.  Delegation in OOP can therefore be said to realize the benefits
of code reuse, but not in an official OOP code reuse manner that is usually implied as a reason for implementing traditional OOP inheritance. Instead of creating new code in a parent or Superclass that can then be reused by a child or subclass object, delegation invokes services from other objects, thereby taking advantage of code that already exists to support the other existing object.  


*''If you find that a class needs to hide a function or a attribute from a super-class, then it is not a right candidate for sub-classing. Delegation is an appropriate solution for this situation.''
=== Delegation Example ===


*''You should not subclass a Utility class in to a problem domain specific class. By doing so, you would run the risk that your classes may have to be changed due to changes in Utility class that you have no control over. Though the risk may be small, there is no benefit of using Inheritance in this situation. Delegation is the appropriate solution for this.''
A classic example of Delegation would be a university setting that involves people who can be students at the university as well as employees of the university. The diagram below illustrates an inheritance view of these relationships:


*''Well, we do have certain disadvantages of using Delegation over Inheritance. For example, designs based on Delegation have the following disadvantages over inheritance:''
[[Image:oop-pic6.jpg|frame|center|Figure 5. University Inheritance Scenario]]


*''Delegation results in smaller number of classes, but a larger number of objects in memory. This could result in performance issue when the objects in memory grow due to inefficient handling or disposal of objects.''
In the above diagram, if a person was both a student and also an employee of the university, there would be three objects representing the person: a person Superclass object, and then student and employee subclass objects. If the person's relationship status would change over time due to graduation or quitting their job, the task of keeping all three objects for the person updated could become difficult.


*''There is an extra level of object indirection due to Delegation. It makes the code more difficult to read, understand and debug.''
However using delegation, a person object could be created for each real person associated with the university, and then different objects representing the 'role' the person currently assumes could also be created. A delegation diagram is shown below:


*''But still, Delegation would be the right choice for re-usability, extendibility and maintainability of the system.''
[[Image:oop-pic7.jpg|frame|center|Figure 5. University Delegation Scenario]]
 
In the above diagram, any time the student or employee object needed address information for a person, the student or employee object would would send a message to the person object to obtain this information. The address information would not be duplicated in each of the three person, student, and employee objects associated with the real person.  As a person's roles change within the university, such as the person becoming an instructor, creating a new object to handle the role of instructor would be easier using delegation than it would be using inheritance.
 
== Conclusions ==
 
This topic briefly described several alternate OOP design techniques that can be considered versus the traditional Inheritance OOP design technique.  Programmers should explore the options of Decomposition, Message Forwarding, and Delegation when designing OOP applications.  Initial temptations to aggressively use Inheritance techniques for an application may backfire if the application survives for a long time after initial development.


== '''References:''' ==
== '''References:''' ==
[1] [http://www.javaworld.com:80/javaworld/jw-11-1998/jw-11-techniques.html http://www.javaworld.com:80/javaworld/jw-11-1998/jw-11-techniques.html]
[1] [http://www.javaworld.com:80/javaworld/jw-11-1998/jw-11-techniques.html http://www.javaworld.com:80/javaworld/jw-11-1998/jw-11-techniques.html]


[2] [http://www.javaworld.com:80/javaworld/jw-11-1998/jw-11-techniques.html http://www.javaworld.com:80/javaworld/jw-11-1998/jw-11-techniques.html]
[2] [http://www.atomicobject.com/pages/Motivation+for+OO http://www.atomicobject.com/pages/Motivation+for+OO]  


[3] [http://www.javaworld.com:80/javaworld/jw-11-1998/jw-11-techniques.html http://www.javaworld.com:80/javaworld/jw-11-1998/jw-11-techniques.html]
[3] [http://www.javaworld.com:80/javaworld/jw-11-1998/jw-11-techniques.html http://www.javaworld.com:80/javaworld/jw-11-1998/jw-11-techniques.html]
 
[4] [http://www.javaworld.com:80/javaworld/jw-11-1998/jw-11-techniques.html http://www.javaworld.com:80/javaworld/jw-11-1998/jw-11-techniques.html]
[4] [http://developer.apple.com/legacy/mac/library/documentation/Cocoa/Conceptual/OOPandObjC1/OOPandObjC1.pdf http://developer.apple.com/legacy/mac/library/documentation/Cocoa/Conceptual/OOPandObjC1/OOPandObjC1.pdf]


[5] [http://macdevcenter.com/lpt/a/3642 "Objective-C Dynamite!" http://macdevcenter.com/lpt/a/3642 "Objective-C Dynamite!"]
[5] [http://macdevcenter.com/lpt/a/3642 "Objective-C Dynamite!" http://macdevcenter.com/lpt/a/3642 "Objective-C Dynamite!"]


[6] [http://developer.apple.com/legacy/mac/library/documentation/Cocoa/Conceptual/OOPandObjC1/OOPandObjC1.pdf http://developer.apple.com/legacy/mac/library/documentation/Cocoa/Conceptual/OOPandObjC1/OOPandObjC1.pdf]
[6] Larman, Craig. Applying Uml and Patterns. Upper Saddle River: Prentice Hall PTR, 2005.  
[7] [http://developergeeks.com/article/16/when-not-to-use-inheritance-but-delegation http://developergeeks.com/article/16/when-not-to-use-inheritance-but-delegation]
 
[8] Larman, Craig. Applying Uml and Patterns. Upper Saddle River: Prentice Hall PTR, 2005.  


[9] Gamma, Erich. Design Patterns. Boston: Addison-Wesley, 1995.
[7] Gamma, Erich. Design Patterns. Boston: Addison-Wesley, 1995.

Latest revision as of 18:15, 15 October 2010

Decomposition, Message Forwarding, and Delegation versus Inheritance in OOP Design

Most OOP overviews explain what the concept of Inheritance is in Object Oriented Programming, and how Inheritance works, but not many OOP overviews discuss alternatives when Inheritance may not be the best design choice for an OO application. This topic briefly introduces the OOP concepts of Inheritance, Decomposition, Message Forwarding, and Delegation, and then discusses when the concepts could be considered as potential alternatives to the use of Inheritance for some OOP applications.

Beginning OOP developers often feel compelled to use Inheritance aggressively to achieve the advertised benefit of code re-use for their applications. However the full life cycle of an OO application needs to be considered. Developers usually agree more coding time will be spent maintaining and enhancing an OO application after it is developed, compared to the coding time needed to initially develop the application. In this context, it is often better to use Decomposition, Message Forwarding, and Delegation techniques instead of inheritance techniques for an application. These techniques can provide more flexibility in the maintenance and enhancement aspects of an OO application.

It needs to be noted that different OOP authors interchange and blur the meaning of what is meant by Decomposition, Message Forwarding, and Delegation in OOP. In general, discussions about Decomposition, Message Forwarding, and Delegation in the context of OOP are suggesting alternative ways to accomplish OOP designs other than utilizing a traditional OOP design that uses pure Inheritance principles.

Inheritance in OOP Design

Inheritance is one of the key concepts in Object Oriented Programming. In OOP, parent classes describe and define attributes and capabilities of objects in a parent class. Subsequently subclasses can be defined to create objects that inherit or acquire attributes and capabilities from the parent class. Parent classes are also known as the Superclass of the child, or subclass. Programming code that is created to describe the attributes and methods of objects in a parent class can easily be reused by objects that are created in subclasses of the parent class. Using inheritance in a OO application is appropriate when the relationship between the Superclass and subclass is a 'Is-A' relationship.

Inheritance Example

Consider the concept of single inheritance through class extension [1]

class Fruit {
    //...
}
class Apple extends Fruit {
    //...
}

In the code above, class Apple is related to class Fruit by inheritance, because Apple extends Fruit. In this example, Fruit is the superclass and Apple is the subclass.

Figure 1. The inheritance relationship

Decomposition in OOP Design

A useful starting definition for Decomposition in OOP is "In OO decomp, we think in terms of identifying active, autonomous agents which are responsible for doing the work, and which communicate with each other to get the overall problem solved." [2]

The 'autonomous agents' mentioned above would correspond to designing several different objects that each have suitable functionality that together allow a task or process to be performed, rather than taking the time to design a grand Superclass and subclass inheritance hierarchy which contains one or a few overly complex objects that are required to address a given task or process. Mixins in Ruby on Rails would be an example of decomposition in OOP, since Mixins allow useful methods from classes to be easily mixed-in, or used as utility methods by classes that may not be suitably related in a traditional inheritance hierarchy.

Composition in OOP Design

In OOP literature, discussions that mention object oriented decomposition often also discuss the topic of object oriented composition. An alternative to an inheritance relationship is a composition relationship[3], whereby instance variables of one object refer to other objects.

Composition Example

class Fruit {
    //...
}
class Apple {
    private Fruit fruit = new Fruit();
    //...
}

In the example above, class Apple is related to class Fruit by composition, because Apple has an instance variable that holds a reference to a Fruit object. The UML diagram for this composition relationship is shown below in Figure 2.


Figure 2. The composition relationship

In this example, Apple can be called the front-end class, and Fruit is called the back-end class. In a composition relationship, the front-end class holds a reference in one of its instance variables to a back-end class. With inheritance relationships, it is usually easy to add new subclasses, however changes made to the interface of the Superclass in an inheritance relationship are often said to be 'fragile', because such changes may cause problems for subclasses in the relationship, and at times cause 'rippled' problems for all subclasses of the superclass.

How do composition and inheritance compare with each other?

  • It is easier to change the interface of a back-end class (composition) than a superclass (inheritance). As the previous example illustrated, a change to the interface of a back-end class necessitates a change to the front-end class implementation, but not necessarily the front-end interface. Code that depends only on the front-end interface still works, so long as the front-end interface remains the same. By contrast, a change to a superclass's interface can not only ripple down the inheritance hierarchy to subclasses, but can also ripple out to code that uses just the subclass's interface.
  • It is easier to change the interface of a front-end class (composition) than a subclass (inheritance). Just as superclasses can be fragile, subclasses can be rigid. You can't just change a subclass's interface without making sure the subclass's new interface is compatible with that of its supertypes. For example, you can't add to a subclass a method with the same signature but a different return type as a method inherited from a superclass. Composition, on the other hand, allows you to change the interface of a front-end class without affecting backend classes.
  • Composition allows you to delay the creation of back-end objects until (and unless) they are needed, as well as changing the back-end objects dynamically throughout the lifetime of the front-end object. With inheritance, you get the image of the superclass in your subclass object image as soon as the subclass is created, and it remains part of the subclass object throughout the lifetime of the subclass.

Message Forwarding in OOP Design

Message forwarding between objects, as implemented in dynamic typing OO languages such as Objective-C, provides an alternative to inheritance relationships between objects that are statically determined at compile time. Using message sending, an object can always forward a message to another object in case it does not know how to handle the message. Alternately, an object can forward a message to another object intentionally, based on some criteria within the message. In OOP message forwarding incorporates concepts that are used to describe delegation (see next section) and composition.

Message Forwarding Example

Figure 3 below is a depiction of message forwarding, as implemented by the language Objective-C.

Figure 3. Message Forward in the language Objective-C

In this illustration, an object sends a negotiate type of message to the Warrior object, expecting the Warrior object to fully handle and manage this negotiate message request. However, unknown to the original object sender, when the Warrior object receives this message, the Warrior object chooses to forward the negotiate message on to an object from the Diplomat class. The Diplomat object processes the negotiate message, and sends it's response back to the Warrior object, which in turn returns the message response back to the original sender. In this illustration the Warrior object explicitly recognized the negotiate message should be sent to the Diplomat object to be processed. Other OOP message forwarding techniques, such as implemented in Objective-C, allow objects to forward any 'unrecognized' messages they have received to objects that specifically exist to process unexpected messages. [4]

Message forwarding in a dynamic language like Objective-C can provide better design time flexibility and run time flexibility to applications versus a traditional inheritance design and implementation. During the development of an OO application, many design issues are not yet fully determined. It's a lot easier to work out how your framework is supposed to behave without having to start out in a most-factored state, with plenty of empty interface fluff. A dynamic language like Objective-C lets you use static typing as well, so once you settle on a design, you can declare your types with no penalty. Best of both worlds. "Objective-C Dynamite!" [5]

Delegation in OOP Design

Delegation is defined as: The notion that an object can issue a message to another object in response to a message. The first object therefore delegates the responsibility to the second object [6]. The Gang of Four discuss delegation in their introduction to the Design Patterns book [7]. Delegation is a way of making composition as powerful for reuse as inheritance.

Basics of delegation

Two objects are involved in delegation. There is the object that receives a message, and the object that it delegates the responsibility to. It can be shown in a UML class diagram as the following:

Figure 4. Delegation in OOP

Relation to inheritance

Delegation is often used instead of inheritance to produce robust designs. With inheritance, a subclass will often "delegate" the response to a specific message to the parent class as shown in the following diagram:

Figure 5. Inheritance and Delegation in OOP

In this case, when the doSomething() method is invoked upon the Child object, it delegates the handling of the message to its parent.

Static vs. Dynamic delegation

Inheritance represents a static relationship between classes. If you have child Class S that is a subclass of a parent Class P, you cannot change that relationship. Any instance of S will inherit the behaviors and members of P. Once the relationship is defined, you cannot change it. Clearly, the subclass can override the behavior, but again, this is static. The actual method that will handle the message is known at compile time.

Delegation, on the other hand, is a dynamic relationship. You can change the object that is the target of the delegation. That is, the receiver of the message can decide who should handle the message. Delegation in OOP strives to allow one object to reuse functionality that another object can readily provide. Delegation in OOP can therefore be said to realize the benefits of code reuse, but not in an official OOP code reuse manner that is usually implied as a reason for implementing traditional OOP inheritance. Instead of creating new code in a parent or Superclass that can then be reused by a child or subclass object, delegation invokes services from other objects, thereby taking advantage of code that already exists to support the other existing object.

Delegation Example

A classic example of Delegation would be a university setting that involves people who can be students at the university as well as employees of the university. The diagram below illustrates an inheritance view of these relationships:

Figure 5. University Inheritance Scenario

In the above diagram, if a person was both a student and also an employee of the university, there would be three objects representing the person: a person Superclass object, and then student and employee subclass objects. If the person's relationship status would change over time due to graduation or quitting their job, the task of keeping all three objects for the person updated could become difficult.

However using delegation, a person object could be created for each real person associated with the university, and then different objects representing the 'role' the person currently assumes could also be created. A delegation diagram is shown below:

Figure 5. University Delegation Scenario

In the above diagram, any time the student or employee object needed address information for a person, the student or employee object would would send a message to the person object to obtain this information. The address information would not be duplicated in each of the three person, student, and employee objects associated with the real person. As a person's roles change within the university, such as the person becoming an instructor, creating a new object to handle the role of instructor would be easier using delegation than it would be using inheritance.

Conclusions

This topic briefly described several alternate OOP design techniques that can be considered versus the traditional Inheritance OOP design technique. Programmers should explore the options of Decomposition, Message Forwarding, and Delegation when designing OOP applications. Initial temptations to aggressively use Inheritance techniques for an application may backfire if the application survives for a long time after initial development.

References:

[1] http://www.javaworld.com:80/javaworld/jw-11-1998/jw-11-techniques.html

[2] http://www.atomicobject.com/pages/Motivation+for+OO

[3] http://www.javaworld.com:80/javaworld/jw-11-1998/jw-11-techniques.html

[4] http://developer.apple.com/legacy/mac/library/documentation/Cocoa/Conceptual/OOPandObjC1/OOPandObjC1.pdf

[5] "Objective-C Dynamite!" http://macdevcenter.com/lpt/a/3642 "Objective-C Dynamite!"

[6] Larman, Craig. Applying Uml and Patterns. Upper Saddle River: Prentice Hall PTR, 2005.

[7] Gamma, Erich. Design Patterns. Boston: Addison-Wesley, 1995.