CSC/ECE 517 Fall 2010/ch3 S30 RJ

From Expertiza_Wiki
Revision as of 01:54, 6 October 2010 by Rsjohns3 (talk | contribs)
Jump to navigation Jump to search

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 Decomposition, Message Forwarding, and Delegation could be considered as potential alternatives to the use of Inheritance for some OOP applications.

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 the attributes and capabilities of objects that belong to the parent class, or Superclass of the child, or subclass. Programming code that is created by programmers 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 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." [1]

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 a inheritance relationship is a composition relationship[2], whereby instance variables of an object refer to other objects.

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

The author of the article used as the basis for my material 3 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"

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.

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-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.
  • 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

Delegation in OOP Design

Delegation in OOP is a technique or strategy whereby an object relies upon or receives services from another object in order to accomplish a task or process, rather than having an object have all required services or methods defined within itself. 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. The functionality available in the use of mixins in Ruby on Rails would be an example of delegation in OOP.

References:

References: <references/>