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

From Expertiza_Wiki
Jump to navigation Jump to search
No edit summary
 
(17 intermediate revisions by 2 users not shown)
Line 3: Line 3:
== Introduction ==
== Introduction ==


Object Oriented Programming concentrates on the design of a system using objects as individual entities which has different states and behaviors. These objects interact with each other to form the system. Object Oriented Design (OOD) refers to the process of deciding the objects and the level of interaction between these.
Object Oriented Programming concentrates on the design of a system using objects as individual entities which have different states and behaviors. Object Oriented Design (OOD) refers to the process of designing the objects and the level of interaction between those.


Object Oriented Approach has advantages such as the code is easy to maintain and it can be reused. Thus it ensures minimal duplication of code in your system and improves the quality of the software.
Object Oriented Approach has advantages such as code is easy to maintain and it can be reused. Thus it ensures minimal duplication in system and improves the quality of the software. When we think of object oriented programming, Inheritance, Encapsulation, Polymorphism features attract our attention. However, there are many other alternatives to the traditional inheritance such as Delegation, Message Forwarding and Decomposition.
When we think of object oriented programming, the Inheritance, Encapsulation, Polymorphism feature attracts our attention. However, there are many other alternatives to the traditional inheritance such as Delegation, Message Forwarding and decomposition.  
 
Inheritance is the mechanism by which we can extract the common functionality between hierarchical classes, hence a method can be implemented only once in a hierarchical class system and can be accessed through it descendant. Delegation deals with passing the execution of program to another class at run time.
Inheritance is the mechanism by which we can extract common functionality between hierarchical classes. A method can be implemented only once in a hierarchical class system and can be accessed through its descendants. Delegation deals with passing the execution control of method to another class at run time. Message Forwarding facilitates communication between different objects by sending and receiving messages. Decomposition is the technique of breaking complex problem into smaller parts so that they can be easily implemented and managed.
With the help of Message Forwarding an object can communicate with other objects by sending and receiving messages. Decomposition is the technique of breaking a task into smaller parts so that they can be easily implemented and managed.
 
In this chapter, we will explain the concept of Inheritance first, later we will contrast it with Delegation, Message Forwarding and Decomposition.  


In this chapter, we will be first explaining the concept of Inheritance. Further, we will be contrasting it with Delegation, Message Forwarding and Decomposition.


== Inheritance ==
== Inheritance ==


Inheritance is a powerful concept in object oriented programming which allows code reusability. In this the functionality of the base class can be inherited by its subclasses. At the same time subclass can have its own functionality.
Inheritance is a powerful concept in Object Oriented Programming which allows code reuse. In this, functionality of base class can be inherited by its subclasses. At the same time a subclass can have its own functionality.


There two types of inheritance   
There two types of inheritance   
Line 24: Line 24:
'''Multiple Inheritance'''
'''Multiple Inheritance'''


This property allows a single class to inherit functionality from multiple classes. C++ allows Multiple Inheritance, however, the major drawback of this property is that it leads to the classical [http://en.wikipedia.org/wiki/Diamond_problem Diamond Multiple Inheritance Problem].  
This property allows a single class to inherit functionality from multiple classes. C++ allows Multiple Inheritance, however, the major drawback of this property is that it leads to the classical [http://en.wikipedia.org/wiki/Diamond_problem Diamond Multiple Inheritance Problem]. This is a primary reason why most of the other Object Oriented languages do not support Multiple Inheritance.
   
   
The concept of Inheritance can be better explained with a code in Ruby.  
The concept of Inheritance can be better explained with a code in Ruby.  
Line 53: Line 53:
</pre>
</pre>


Here the class Herbivorous inherits the class Animal. So when we create an object of the Herbivorous class we are able to inherit methods from the Animal class. Here is the simple UML diagram to explain inheritance.
Here the class Herbivorous inherits class Animal. So when we create an object of the Herbivorous class we are able to inherit methods from the Animal class. Here is the simple UML diagram to explain inheritance.


[[Image:Inheritance-s30.jpg|frame|center|UML diagram for inheritance]]
[[Image:Inheritance-s30.jpg|frame|center|UML diagram for inheritance]]
Line 67: Line 67:
== Delegation ==
== Delegation ==


As the word suggest, delegation means assign the task to some other entity. In object oriented programming terminology it an be explained as, process of implementation of one method to other method in different class. Delegation is also called as dynamic inheritance.
As the word suggests, delegation means assigning the task to some other entity. In Object Oriented Programming terminology, it can be explained as, the process of passing execution control from one method to other method which can be in a different class. Delegation is also called as Dynamic Inheritance.


When two objects are not of the same type, let’s say there are two objects A and B, A has some method and attributes, which B is interested in using. Then, using delegation technique,
Let’s say there are two objects A and B, A has some method and attributes, which B is interested in using. Thus, when two objects are not of the same type, above functionality can be achieved through Delegation. This mechanism is suitable when we have to decide which functionality needs to be invoked based on user input at run time.
This mechanism can be suitable where we have to decide at run time based on the user input, which functionality needs to be executed.


''Code example in Ruby''
''Code example in Ruby''
Line 115: Line 114:
</pre>
</pre>


The above Ruby example creates a class ''Airplane'' which has method ''motion'' in it. This method throws ''NotImplementedError'' which indicates that there is not definition for this method. The classes ''Wheel'',''Wing'' and ''AirplaneActivity'' includes the class ''Airplane'' in their definition and overrides the ''motion'' method. In the class ''AirplaneActivity'', we provide the ''initialize'' method, which initializes the instance variable to an object of class ''Wing''. Also the methods ''setWing'' and ''setWheel'' in the sets the different object values for instance variable ''@var''.
The above Ruby example creates a class ''Airplane'' which has method ''motion'' in it. This method throws ''NotImplementedError'' which indicates that there is no definition for this method. The classes ''Wheel'',''Wing'' and ''AirplaneActivity'' include the class ''Airplane'' in their definition and override ''motion'' method. In the class ''AirplaneActivity'', we provide ''initialize'' method, which initializes the instance variable to an object of class ''Wing''. Also, the methods ''setWing'' and ''setWheel'' set different object values for instance variable ''@var''.


<pre>
<pre>
Line 126: Line 125:
</pre>
</pre>


The above code is a sample test code which will reveal the Delegation behavior. In the first like we have created an object obj of class ''AirPlaneActivity''. This calls the ''initialized'' method and creates a ''Wing'' class object. Next line executes ''motion'' method of class ''AirPlaneActivity'' which calls ''motion'' method of ''Wing'' class. This is possible because the current value of ''@var'' points to object of ''Wing'' class. In the third line, using ''setWheel'', the ''@var'' value is set to ''Wheel's'' object. Due to this the ''motion'' method of ''Wheel'' class will get invoked. In this way, ''motion'' method of ''AirPlaneActivity'' class delegates control to different ''motion'' method of ''Wing'' and ''Wheel'' class based on the value of ''@var'' instance variable.  
The above code is a sample test code which will reveal the Delegation behavior. The first statement creates an object obj of class ''AirPlaneActivity''. This calls the ''initialize'' method and creates a ''Wing'' class object. Next line invokes ''motion'' method of class ''AirPlaneActivity'' which in turn calls ''motion'' method of ''Wing'' class. This is possible because the current value of ''@var'' points to object of ''Wing'' class. In third line, using ''setWheel'', the ''@var'' value is set to ''Wheel's'' object. Due to this the ''motion'' method of ''Wheel'' class will get invoked. In this way, ''motion'' method of ''AirPlaneActivity'' class delegates control to different ''motion'' methods of ''Wing'' and ''Wheel'' class based on the value of ''@var'' instance variable.  




[[Image:DelegationUML-s30.jpg|frame|center|UML diagram for delegation]]
[[Image:DelegationUML-s30.jpg|frame|center|UML diagram for delegation]]


Above [http://en.wikipedia.org/wiki/Unified_Modeling_Language UML] diagram shows how the delegation pattern looks like and how the behaviors be composed at the run-time. It also shows how all the classes are related to each other.  
Above [http://en.wikipedia.org/wiki/Unified_Modeling_Language UML] diagram depicts the delegation pattern and its behavior at run-time. It also shows how all the classes are related to each other.  


'''Delegation Vs. Inheritance'''
'''Delegation Vs. Inheritance'''


* Delegation is used when there is HAS-A relationship between two object. For example, Airplane has engine and wings. Relationship between all these three classes depict the HAS-A relationship. Based on which activity to be monitored, corresponding motion() method is invoked. This is not the case with the inheritance, to get the slightly different functionality, we have to over write the existing method.
* Delegation is used when there is HAS-A relationship between two objects. For example, Airplane has an engine and wings. Relationship between all these three classes depicts the HAS-A relationship. Based on which activity to be monitored, corresponding motion() method is invoked. This is not the case with Inheritance, where to get a slightly different functionality, we have to over write the existing method.


* Delegation should be used when there the two objects are of the different type but has some what similar functionality.  
* Delegation should be used when the two objects are of different types but have some similar functionality.  


* Delegation can be used where components that behave identically, but this situation may change over the time.
* Delegation can be used where components behave identically, but this situation may change over time.


* Delegation leverage loose coupling in the components and hence the alteration of one component does not affect the system much. In the inheritance, on the other hand, there is a tight coupling between parent class and its children hence modification in parents functionality may lead logical error in all its children.  
* Delegation leverages loose coupling in components and hence alteration of one component does not affect the system much. In Inheritance, on the other hand, there is a tight coupling between parent class and its children. Hence modification in parent's functionality may lead to logical error in all its children.  


* Delegation is sometimes called as dynamic inheritance as the binding happens at run time. In the above example, based on the object, corresponding methods are invoke. This not the case with inheritance as its is determined at the compile time.  
* Delegation is sometimes called as Dynamic Inheritance as binding happens at run time. In above example, based on the object, corresponding methods are invoked. This is not the case with Inheritance as the objects to be invoked are determined during compilation.  




'''Pitfalls of delegation'''  
'''Pitfalls of delegation'''  


* Delegation increases the code size as compared to inheritance. In above code also we have written one interface and three classes to make delegation work.
* Delegation increases code size as compared to Inheritance. In above code, we have written one interface and three classes to explain the concept of delegation.


== Message Forwarding ==
== Message Forwarding ==


In object oriented programming, message forwarding is one of the most powerful techniques. In this, when a message is sent to an object and if that object does not process that particular method, then instead of displaying error, receiving object gets a second chance to process a message.   
In Object Oriented Programming, Message Forwarding is one of the most powerful techniques. In this, when a message is sent to an object and if that object does not process that particular method, then instead of displaying an error, receiving object gets a second chance to process a message.   


[http://en.wikipedia.org/wiki/Smalltalk SmallTalk] and [http://en.wikipedia.org/wiki/Objective-C Objective-C] supports this mechanism.  
[http://en.wikipedia.org/wiki/Smalltalk SmallTalk] and [http://en.wikipedia.org/wiki/Objective-C Objective-C] supports this mechanism.  


Let’s look at the simple example for message forwarding. Lets say we have two classes ‘Nurse’ and ‘Doctor’.
Let’s look at a simple example which explains Message Forwarding. Say we have two classes ‘Nurse’ and ‘Doctor’.


[[Image:Msgfws30.jpg|frame|center|Message forwarding ]]
[[Image:Msgfws30.jpg|frame|center|Message forwarding ]]


In the above example, when the message for ‘operate’ comes to the Nurse, it does not handle that messages as ‘Nurse don’t do operations alone and has to be done by doctor.’ Hence instead of giving a error, it forwards that message along with input parameters to the ‘Doctor’ object.  Doctor object handles the operate method and return the response to the Nurse object. From the abstract view, it appears that Nurse object is only handling the message but in real, Doctor’s object working on it. Message forwarding simulates multiple inheritances in object oriented programming as object that forwards message can inherits method from its super class and the class where it is forwarding the method. In other words, Nurse inherits functionality from its super class and Doctor class.
In above example, when the message for ‘operate’ comes to Nurse, it does not handle those messages as ‘Nurse don’t do operations alone and has to be done by doctor.’ Hence instead of giving an error, it forwards that message along with input parameters to the ‘Doctor’ object.  Doctor object handles operate method and return the response to Nurse object. From an abstract view, it appears that Nurse object is handling the message, but in reality, Doctor’s object is working on it. Message Forwarding simulates Multiple Inheritances in Object Oriented Programming as object that forwards message can inherit method from its super class and the class where it is forwarding the method. In other words, Nurse inherits functionality from its super class and Doctor class.


For selection of different objects to forward the message, observer design pattern is used. Languages like [http://en.wikipedia.org/wiki/Objective-C objective-C] have extensive support for message forwarding. To implement this, [http://en.wikipedia.org/wiki/Observer_pattern observer design pattern] is used.
For selection of different objects to forward the message, observer design pattern is used. Languages like [http://en.wikipedia.org/wiki/Objective-C objective-C] have extensive support for message forwarding. To implement this, [http://en.wikipedia.org/wiki/Observer_pattern observer design pattern] is used.
Line 184: Line 182:
</pre>
</pre>


In the above code, newObject is an object and doSomethingWith is a method definition which unknown to newObject. As the message to object binding happens at run-time, compiler will still compile the code. At run-time objective-C will find out that newObject did not recognize the doSomethingWith: message. Instead of giving error, the runtime sends another message to newObject as forwardInvocation: message. Now if the newObject know the anObject then it will forward. Self here refers to the object which get the message, in the case its newobject.
In above code, newObject is an object and doSomethingWith is a method definition which is unknown to newObject. As the message to object binding happens at run-time, compiler will still compile the code. At run-time objective-C will find out that newObject does not recognize the doSomethingWith: message. Instead of giving error, the runtime sends another message to newObject as forwardInvocation: message. Now if newObject knows the anObject then it will forward. Self here refers to the object which receives the message, in the case its newobject.


Message forwarding can also be simulated from other objected oriented languages.  
Message forwarding can also be simulated from other objected oriented languages.  
Line 226: Line 224:
</pre>
</pre>


In the above code, alert message will send a message to obejct1 for handling 'receivingFunction'. As object1 does not handle it, it will send message to object2 and so on till actual function gets called. To this we have to implement 'forwardInvocation' method for forwarding the message.
In above code, alert message will send a message to obejct1 for handling 'receivingFunction'. As object1 does not handle it, it will send message to object2 and so on till actual function gets called. For this to work, we have to implement 'forwardInvocation' method for forwarding the message.




'''Message forwarding Vs. Inheritance'''
'''Message Forwarding Vs. Inheritance'''


* Message forwarding is leverage the functionality of reusing the code of some other class. This functionality cannot be implemented using inheritance as code reuse is done only in the inheritance hierarchy and not beyond that.
* Message Forwarding leverages functionality of reusing code of some other class. This functionality cannot be implemented using Inheritance as code reuse is done only in the inheritance hierarchy and not beyond that.


*Compared to inheritance, message forwarding provides privilege of loosely coupled design as the different objects can communicate via exchange of massages. In inheritance, there is always a tight coupling .
* Compared to Inheritance, Message Forwarding provides privilege of loosely coupled design as different objects can communicate via exchange of messages. In Inheritance, there is always a tight coupling.


* This helps data to be sent to objects of different classes in a very efficient manner. Languages like objective –C or SmallTalk have specific methods to implement this. Also message forwarding can be simulated with other object oriented languages like Java , Ruby and many more.
* This helps data to be sent to objects of different classes in a very efficient manner. Languages like objective –C or SmallTalk have specific methods to implement this. Also Message Forwarding can be implemented with other object oriented languages like Java , Ruby and many more as shown in example above.


* The change of a state in one object must be reflected in another object without keeping the objects tight coupled.
* The change of a state in one object can be reflected in another object without keeping the objects tightly coupled.


* In inheritance, if we want custom implementation, then we have to override the method to provide new functionality. In this we cannot change the method prototype. If user wants to change prototype of the method, then all the overridden methods have to change the prototypes. In contrast to that, with the help of message forwarding, we can avoid this situation as the messages are getting passed to other object, hence no dependencies.
* In Inheritance, if we want custom implementation, then we have to override a method to provide new functionality, in which a method prototype can not be changed. If user wants to change prototype of a method, then all overridden methods have to be changed. In contrast to that, in Message Forwarding there are no dependencies between communicating objects, so no design changes are required.


'''Pitfalls in Message forwarding'''
'''Pitfalls in Message Forwarding'''
 
*In Message Forwarding, each argument has to allocate extra memory, however in Inheritance,  derived class uses references to arguments passed to the methods of parent class. As an example, if a big string (around 2000 characters) has to be passed as an argument that much memory has to be allocated in Message Forwarding.  However, in Inheritance as all the objects are tightly coupled, sending an object reference is enough.


*In message forwarding, each argument have to allocate extra memory, however in inheritance, as derived class object at call of it parent object only reference to the arguments are passed. As an example, if a big string (around 2000 characters) to be passed as an argument, that much memory has to be allocated.  However, in inheritance as all the objects are tightly coupled, sending the object reference is enough.
== Decomposition ==
== Decomposition ==


Decomposition follows the divide and conquer strategy which help to solve any complex problem. In object oriented terminology, it is a method of breaking down bigger problem or system into smaller classes and object which gives clear understanding of its functionality. The different part after decomposition of the systems interacts with each other through well defined interfaces.
Decomposition follows the divide and conquer strategy which helps to solve complex problems. In object oriented terminology, it is the process of breaking down bigger problem or system into smaller classes and objects which gives clear understanding of its functionality. The different parts of a systems after decomposition interact with each other through well defined interfaces.


In non-object oriented languages such as C, the large systems can be broken down by writing different functions. This is called as functional decomposition. however due to different functions there is tight coupling between all the methods. Hence,if the implementation of one of the function changes, all the other functions which are dependent on this function also need to changed.  
In non-object oriented languages such as C, large systems can be broken down by writing different functions. This is called as functional decomposition, however there exists tight coupling across different functions. In other words, if implementation of one of the function changes, all other functions which are dependent on this function also need to be changed.  


To avoid this problem, Decomposition in object-oriented OOP design is preferred. In this, we create well defined objects, who interacts with each other through well defined interfaces. If the implementation in one of the object changes then, only two objects between which the interaction takes place needs to be changed. In sum, due to changes in one of the objects we do not affect a larger portion of the system.
To avoid this problem, Decomposition in Object-Oriented Design is preferred. In this, we create well defined objects, which interact with each other through well defined interfaces. If the implementation in one of the objects changes then, only two objects between which the interaction takes place need to be changed. In sum, changes in one of the objects does not affect large portion of the system.


[[Image:Decompose-s30.jpg|frame|center|Decomposition]]
[[Image:Decompose-s30.jpg|frame|center|Decomposition]]


For better understanding lets consider the above airplane system example. The airplane consists of wheel, wings, propellers, cockpit, etc.In Object Oriented Decomposition we create objects of these classes. Also we create an object Controller which interacts with all these objects and is responsible for the overall functioning the system. Now if there is a problem with the wheel, this does not affect the functionality of other objects. Only the central controller and the wheel need to implement the necessary changes. This does not affect the functionality of other objects.
The above diagram shows that a real world problem can be broken down in small objects and classes to manage the system.
For better understanding, let's consider the above airplane system example. The airplane consists of wheel, wings, propellers, cockpit, etc. In Object Oriented Decomposition we create objects of these classes. Also we create an object Controller which interacts with all these objects and is responsible for the overall functioning of the system. Now if there is a problem with the wheel, this does not affect functionality of other objects. Only the central controller and the wheel need to implement the necessary changes.
 
Consider a case, when we wish to provide a class for Vehicles. Now, Car and Bus are types of vehicles. But each has its own Seating Capacity and the number of wheels is also different. If we do not have two different classes like Car and bus then, we have to add separate method for each type of vehicle with different names such as carSeatingCapacity(), busSeatingCapacity(), carNoOfWwheels(), and busNoOfWwheels() which will clutter the class.
A better solution is to use Object Oriented Decomposition. In this, we first find a subset of methods or properties that do not interact with the rest of the class. In this case, it would be the Seating Capacity and number of wheels. So, we just provide these methods in an interface and let the classes use this interface. Hence decomposition in different classes helps in reducing complexity of the design. Thus the main class is divided into smaller classes.
 
The example below shows this concept:
 
<pre>
interface Vehicle
{
  void SeatingCapacity ();
  void NoOfwheels();
}
 
class Car implements Vehicle
{
    void SeatingCapacity()
    {
      // implementation for seating capacity of a car
    }
 
    void NoOfwheels()
    {
      // implementation for number of wheels of a car
    }
}
 
class Bus implements Vehicle
{
    void SeatingCapacity()
    {
        // implementation for seating capacity of a bus
    }
 
    void NoOfwheels()
    {
      // implementation for number of wheels of a bus
    }
}
</pre>
 
In above code, we have defined an interface Vehicle. Two classes Car and Bus implement this interface. The SeatingCapacity and NoOfWheels methods are defined in the interface. But, the classes Car and Bus provide their own implementation of these methods. This shows how a system can be decomposed into smaller parts.
One other example of Decomposition is MVC ( Model-View-Controller ) architecture. Here, first we analyze our system and come up with a different controller for each task. Thus, we have a specific controller assigned for certain specific tasks. This makes the implementation easier and easy to manage. The View refers to the interface as seen by a user of the system. The Controller takes the input from user and instructs the model to perform the required actions on it. The Model interacts with the backend database to retrieve and update the required information.
 
In C#, we can break down a class implementation into several class files and you divide your object the way you want.
 


'''Inheritance vs Decomposition'''
'''Inheritance vs Decomposition'''


*In object oriented programming multiple inheritances plays an important role. Languages like C++ allowed multiple inheritances. In the industries, to implement large systems this property is used frequently. However due to well known diamond problem for multiple inheritances and complex hierarchy of the classes programmers finds less visibility of the functionality in inheritance tree. Hence decomposition is considered as a solution to these problems. To solve these issues many researchers such are working on finding reliable algorithms to decompose the large inheritance based hierarchical systems. M. Habib, M. Huchard  and J. Spinrad have covered this aspects in their paper named [http://www.springerlink.com/content/v3384m611187nl12/ A Linear Algorithm To Decompose Inheritance Graphs Into Modules]
*In Object Oriented Programming, Multiple Inheritance plays an important role. Languages like C++ allow Multiple Inheritance. In industry, to implement large systems this property is used frequently. However, due to well known diamond problem for Multiple Inheritance and complex hierarchy of the classes, programmers find less visibility of the functionality in an Inheritance tree. Hence, Decomposition is considered as a solution to these problems. To solve these issues, many researchers are working on finding reliable algorithms to decompose large Inheritance based hierarchical systems. M. Habib, M. Huchard  and J. Spinrad have covered these aspects in their paper named [http://www.springerlink.com/content/v3384m611187nl12/ A Linear Algorithm To Decompose Inheritance Graphs Into Modules]


*In Inheritance we need to define methods in the Super class which can be used by the objects in sub class. This can be achieved by using the extends functionality in Java. Where as in the case of Decomposition, the objects define their own methods. So there is minimal dependency between the interacting objects.
*In Inheritance, we need to define methods in Super class which can be used by objects in sub class. This can be achieved by using the extends functionality in Java. Where as in case of Decomposition, objects define their own methods. So there is minimal dependency between interacting objects.


*When the system is huge and the depth hierarchy tree is large, decomposition should be used.  
*When the system is huge and the depth hierarchy tree is large, decomposition should be used.  


*When we want the components of the system to be loosely coupled. Changes made in one module should not affect the other module. On the other hand, inheritance bind two different classes so that change in one module brake down most of the system.  
*When we want components of the system to be loosely coupled, changes made in one module should not affect other modules. On the other hand, Inheritance binds two different classes so that change in one module breaks down most of the system.
 


== Conclusion ==
== Conclusion ==


Inheritance plays a vital role in object oriented programming to design more structured and reliable system. However there are always some cases where inheritance is not a suitable option.  
Inheritance plays a vital role in object oriented programming to design more structured and reliable systems. However there are always some cases where inheritance is not a suitable option.  


Inheritance is tightly coupled. Inheritance is best suited when the size of the system is small. It is used when we have a IS-A relationship between the classes. As the number of classes in the system grows, we have to look for Decomposition of these classes into smaller ones. This modularizes your system since the objects become loosely coupled.
Inheritance is tightly coupled. Inheritance is best suited when the size of the system is small. It is used when we have a IS-A relationship between the classes. As the number of classes in the system grows, we have to look for Decomposition of these classes into smaller ones. This modularized your system since the objects become loosely coupled.


Delegation is used when we have a HAS-A relation between the classes. Delegation is a type of dynamic binding. Message Forwarding helps to reuse the code of some other class. The messages are passed at run time to invoke methods of other classes.  
Delegation is used when we have a HAS-A relation between the classes. Delegation is a type of dynamic binding. Message Forwarding helps to reuse the code of some other class. The messages are passed at run time to invoke methods of other classes.  

Latest revision as of 21:58, 15 October 2010

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

Introduction

Object Oriented Programming concentrates on the design of a system using objects as individual entities which have different states and behaviors. Object Oriented Design (OOD) refers to the process of designing the objects and the level of interaction between those.

Object Oriented Approach has advantages such as code is easy to maintain and it can be reused. Thus it ensures minimal duplication in system and improves the quality of the software. When we think of object oriented programming, Inheritance, Encapsulation, Polymorphism features attract our attention. However, there are many other alternatives to the traditional inheritance such as Delegation, Message Forwarding and Decomposition.

Inheritance is the mechanism by which we can extract common functionality between hierarchical classes. A method can be implemented only once in a hierarchical class system and can be accessed through its descendants. Delegation deals with passing the execution control of method to another class at run time. Message Forwarding facilitates communication between different objects by sending and receiving messages. Decomposition is the technique of breaking complex problem into smaller parts so that they can be easily implemented and managed.

In this chapter, we will explain the concept of Inheritance first, later we will contrast it with Delegation, Message Forwarding and Decomposition.


Inheritance

Inheritance is a powerful concept in Object Oriented Programming which allows code reuse. In this, functionality of base class can be inherited by its subclasses. At the same time a subclass can have its own functionality.

There two types of inheritance

Single Inheritance

This property restricts that a class can only inherit from one class. Java and Ruby are examples of object-oriented languages that have single inheritance.

Multiple Inheritance

This property allows a single class to inherit functionality from multiple classes. C++ allows Multiple Inheritance, however, the major drawback of this property is that it leads to the classical Diamond Multiple Inheritance Problem. This is a primary reason why most of the other Object Oriented languages do not support Multiple Inheritance.

The concept of Inheritance can be better explained with a code in Ruby. In ruby we have single Inheritance.

Example in Ruby

class Animal
  def sleep
    puts " I want to sleep "
  end
end

class Herbivorous < Animal
  def eat
    puts " I want to eat plants "
   end
 end

Code execution

MyAnimal = Herbivorous.new
MyAnimal.sleep  #I want to sleep
MyAnimal.eat #I want to eat plants

Here the class Herbivorous inherits class Animal. So when we create an object of the Herbivorous class we are able to inherit methods from the Animal class. Here is the simple UML diagram to explain inheritance.

UML diagram for inheritance

When to use inheritance

  • Inheritance is used when there is an IS-A relationship between two classes. For example, Herbivorous is-an Animal.
  • When code re-usability is desired.
  • Inheritance is used when sub categories can be created from complex class.

Delegation

As the word suggests, delegation means assigning the task to some other entity. In Object Oriented Programming terminology, it can be explained as, the process of passing execution control from one method to other method which can be in a different class. Delegation is also called as Dynamic Inheritance.

Let’s say there are two objects A and B, A has some method and attributes, which B is interested in using. Thus, when two objects are not of the same type, above functionality can be achieved through Delegation. This mechanism is suitable when we have to decide which functionality needs to be invoked based on user input at run time.

Code example in Ruby

module Airplane
  def motion
    raise NotImplementedError.new
  end
 end

class Wheel
  include Airplane
  def motion
    puts " Performs circular motion."
  end
end

class Wing
  include Airplane
 
  def motion
    puts "Performs Vertical motion"
  end
end
  
class AirplaneActivity
  include Airplane
  def initialize
    @var = Wing.new
  end
 
  def motion
    @var.motion
  end
  
  def setWing
    @var = Wing.new
  end
  def setWheel
    @var = Wheel.new
 end
end

The above Ruby example creates a class Airplane which has method motion in it. This method throws NotImplementedError which indicates that there is no definition for this method. The classes Wheel,Wing and AirplaneActivity include the class Airplane in their definition and override motion method. In the class AirplaneActivity, we provide initialize method, which initializes the instance variable to an object of class Wing. Also, the methods setWing and setWheel set different object values for instance variable @var.


obj = AirplaneActivity.new
obj.motion	# output => "Performs Vertical motion"
obj.setWheel	# create new object of Wheel class
obj.motion	# output => "Performs Circular motion"

The above code is a sample test code which will reveal the Delegation behavior. The first statement creates an object obj of class AirPlaneActivity. This calls the initialize method and creates a Wing class object. Next line invokes motion method of class AirPlaneActivity which in turn calls motion method of Wing class. This is possible because the current value of @var points to object of Wing class. In third line, using setWheel, the @var value is set to Wheel's object. Due to this the motion method of Wheel class will get invoked. In this way, motion method of AirPlaneActivity class delegates control to different motion methods of Wing and Wheel class based on the value of @var instance variable.


UML diagram for delegation

Above UML diagram depicts the delegation pattern and its behavior at run-time. It also shows how all the classes are related to each other.

Delegation Vs. Inheritance

  • Delegation is used when there is HAS-A relationship between two objects. For example, Airplane has an engine and wings. Relationship between all these three classes depicts the HAS-A relationship. Based on which activity to be monitored, corresponding motion() method is invoked. This is not the case with Inheritance, where to get a slightly different functionality, we have to over write the existing method.
  • Delegation should be used when the two objects are of different types but have some similar functionality.
  • Delegation can be used where components behave identically, but this situation may change over time.
  • Delegation leverages loose coupling in components and hence alteration of one component does not affect the system much. In Inheritance, on the other hand, there is a tight coupling between parent class and its children. Hence modification in parent's functionality may lead to logical error in all its children.
  • Delegation is sometimes called as Dynamic Inheritance as binding happens at run time. In above example, based on the object, corresponding methods are invoked. This is not the case with Inheritance as the objects to be invoked are determined during compilation.


Pitfalls of delegation

  • Delegation increases code size as compared to Inheritance. In above code, we have written one interface and three classes to explain the concept of delegation.

Message Forwarding

In Object Oriented Programming, Message Forwarding is one of the most powerful techniques. In this, when a message is sent to an object and if that object does not process that particular method, then instead of displaying an error, receiving object gets a second chance to process a message.

SmallTalk and Objective-C supports this mechanism.

Let’s look at a simple example which explains Message Forwarding. Say we have two classes ‘Nurse’ and ‘Doctor’.

Message forwarding

In above example, when the message for ‘operate’ comes to Nurse, it does not handle those messages as ‘Nurse don’t do operations alone and has to be done by doctor.’ Hence instead of giving an error, it forwards that message along with input parameters to the ‘Doctor’ object. Doctor object handles operate method and return the response to Nurse object. From an abstract view, it appears that Nurse object is handling the message, but in reality, Doctor’s object is working on it. Message Forwarding simulates Multiple Inheritances in Object Oriented Programming as object that forwards message can inherit method from its super class and the class where it is forwarding the method. In other words, Nurse inherits functionality from its super class and Doctor class.

For selection of different objects to forward the message, observer design pattern is used. Languages like objective-C have extensive support for message forwarding. To implement this, observer design pattern is used. There are four parts to a message: a receiver object, a method name, optional method arguments, and an optional return value are to be handled.

Code example – objective -C


id newObject;                                 // could be any object;
  newObject = [[NewObject alloc] init];       // create and initialize the object
  [newObject doSomethingWith: anotherObject]; // send it a message.


- (void) forwardInvocation: (NSInvocation*)anInvocation
{
  if ([anObject respondsToSelector: [anInvocation selector]])
    return [anInvocation invokeWithTarget: anObject];
  else
    return [self doesNotRecognizeSelector: [anInvocation selector]];
}

In above code, newObject is an object and doSomethingWith is a method definition which is unknown to newObject. As the message to object binding happens at run-time, compiler will still compile the code. At run-time objective-C will find out that newObject does not recognize the doSomethingWith: message. Instead of giving error, the runtime sends another message to newObject as forwardInvocation: message. Now if newObject knows the anObject then it will forward. Self here refers to the object which receives the message, in the case its newobject.

Message forwarding can also be simulated from other objected oriented languages.

Example using JavaScript


// This code will instantiated object 'receiver' 
// and call method 'receivingFunction'

var  receiver = new someClass();
m(receiver, 'receivingFunction', someArgument);

// object implements 'forwardInvocation' to send the message 
// to next object.

var class1 = new Class({
    forwardInvocation: function(){
        return object2;          // Forwarding
    }
});

var class2 = new Class({
    forwardInvocation: function(){
        return object3;         // Forwarding
    }
});

var class3 = new Class({
    receivingFunction: function(){
        return 'Message received.' //Actual message
    }
});

object1 = new class1();
object2 = new class2();
object3 = new class3();

alert(m(object1, 'receivingFunction'));

In above code, alert message will send a message to obejct1 for handling 'receivingFunction'. As object1 does not handle it, it will send message to object2 and so on till actual function gets called. For this to work, we have to implement 'forwardInvocation' method for forwarding the message.


Message Forwarding Vs. Inheritance

  • Message Forwarding leverages functionality of reusing code of some other class. This functionality cannot be implemented using Inheritance as code reuse is done only in the inheritance hierarchy and not beyond that.
  • Compared to Inheritance, Message Forwarding provides privilege of loosely coupled design as different objects can communicate via exchange of messages. In Inheritance, there is always a tight coupling.
  • This helps data to be sent to objects of different classes in a very efficient manner. Languages like objective –C or SmallTalk have specific methods to implement this. Also Message Forwarding can be implemented with other object oriented languages like Java , Ruby and many more as shown in example above.
  • The change of a state in one object can be reflected in another object without keeping the objects tightly coupled.
  • In Inheritance, if we want custom implementation, then we have to override a method to provide new functionality, in which a method prototype can not be changed. If user wants to change prototype of a method, then all overridden methods have to be changed. In contrast to that, in Message Forwarding there are no dependencies between communicating objects, so no design changes are required.

Pitfalls in Message Forwarding

  • In Message Forwarding, each argument has to allocate extra memory, however in Inheritance, derived class uses references to arguments passed to the methods of parent class. As an example, if a big string (around 2000 characters) has to be passed as an argument that much memory has to be allocated in Message Forwarding. However, in Inheritance as all the objects are tightly coupled, sending an object reference is enough.

Decomposition

Decomposition follows the divide and conquer strategy which helps to solve complex problems. In object oriented terminology, it is the process of breaking down bigger problem or system into smaller classes and objects which gives clear understanding of its functionality. The different parts of a systems after decomposition interact with each other through well defined interfaces.

In non-object oriented languages such as C, large systems can be broken down by writing different functions. This is called as functional decomposition, however there exists tight coupling across different functions. In other words, if implementation of one of the function changes, all other functions which are dependent on this function also need to be changed.

To avoid this problem, Decomposition in Object-Oriented Design is preferred. In this, we create well defined objects, which interact with each other through well defined interfaces. If the implementation in one of the objects changes then, only two objects between which the interaction takes place need to be changed. In sum, changes in one of the objects does not affect large portion of the system.

Decomposition

The above diagram shows that a real world problem can be broken down in small objects and classes to manage the system. For better understanding, let's consider the above airplane system example. The airplane consists of wheel, wings, propellers, cockpit, etc. In Object Oriented Decomposition we create objects of these classes. Also we create an object Controller which interacts with all these objects and is responsible for the overall functioning of the system. Now if there is a problem with the wheel, this does not affect functionality of other objects. Only the central controller and the wheel need to implement the necessary changes.

Consider a case, when we wish to provide a class for Vehicles. Now, Car and Bus are types of vehicles. But each has its own Seating Capacity and the number of wheels is also different. If we do not have two different classes like Car and bus then, we have to add separate method for each type of vehicle with different names such as carSeatingCapacity(), busSeatingCapacity(), carNoOfWwheels(), and busNoOfWwheels() which will clutter the class.

A better solution is to use Object Oriented Decomposition. In this, we first find a subset of methods or properties that do not interact with the rest of the class. In this case, it would be the Seating Capacity and number of wheels. So, we just provide these methods in an interface and let the classes use this interface. Hence decomposition in different classes helps in reducing complexity of the design. Thus the main class is divided into smaller classes.

The example below shows this concept:

interface Vehicle
{
  void SeatingCapacity ();
  void NoOfwheels();
}

class Car implements Vehicle
{
     void SeatingCapacity()
     {
      // implementation for seating capacity of a car
     }

     void NoOfwheels()
     {
       // implementation for number of wheels of a car
     }
} 

class Bus implements Vehicle
{
    void SeatingCapacity()
    {
        // implementation for seating capacity of a bus
    }

    void NoOfwheels()
    { 
      // implementation for number of wheels of a bus
    }
} 

In above code, we have defined an interface Vehicle. Two classes Car and Bus implement this interface. The SeatingCapacity and NoOfWheels methods are defined in the interface. But, the classes Car and Bus provide their own implementation of these methods. This shows how a system can be decomposed into smaller parts.

One other example of Decomposition is MVC ( Model-View-Controller ) architecture. Here, first we analyze our system and come up with a different controller for each task. Thus, we have a specific controller assigned for certain specific tasks. This makes the implementation easier and easy to manage. The View refers to the interface as seen by a user of the system. The Controller takes the input from user and instructs the model to perform the required actions on it. The Model interacts with the backend database to retrieve and update the required information.

In C#, we can break down a class implementation into several class files and you divide your object the way you want.


Inheritance vs Decomposition

  • In Object Oriented Programming, Multiple Inheritance plays an important role. Languages like C++ allow Multiple Inheritance. In industry, to implement large systems this property is used frequently. However, due to well known diamond problem for Multiple Inheritance and complex hierarchy of the classes, programmers find less visibility of the functionality in an Inheritance tree. Hence, Decomposition is considered as a solution to these problems. To solve these issues, many researchers are working on finding reliable algorithms to decompose large Inheritance based hierarchical systems. M. Habib, M. Huchard and J. Spinrad have covered these aspects in their paper named A Linear Algorithm To Decompose Inheritance Graphs Into Modules
  • In Inheritance, we need to define methods in Super class which can be used by objects in sub class. This can be achieved by using the extends functionality in Java. Where as in case of Decomposition, objects define their own methods. So there is minimal dependency between interacting objects.
  • When the system is huge and the depth hierarchy tree is large, decomposition should be used.
  • When we want components of the system to be loosely coupled, changes made in one module should not affect other modules. On the other hand, Inheritance binds two different classes so that change in one module breaks down most of the system.

Conclusion

Inheritance plays a vital role in object oriented programming to design more structured and reliable systems. However there are always some cases where inheritance is not a suitable option.

Inheritance is tightly coupled. Inheritance is best suited when the size of the system is small. It is used when we have a IS-A relationship between the classes. As the number of classes in the system grows, we have to look for Decomposition of these classes into smaller ones. This modularized your system since the objects become loosely coupled.

Delegation is used when we have a HAS-A relation between the classes. Delegation is a type of dynamic binding. Message Forwarding helps to reuse the code of some other class. The messages are passed at run time to invoke methods of other classes.

Thus these OOD Principles help to design and implement the system in an effective manner. This article summarizes some of the alternatives to the traditional inheritance. Also we get guidance as when to use inheritance and when to consider different alternatives.


References

Object Oriented Development by Grady Booch - http://www.ics.uci.edu/~taylor/classes/121/BoochOOD003.pdf

Delegation (programming) - http://en.wikipedia.org/wiki/Delegation_%28programming%29

API documentation for Objective-C - http://www.gnustep.org/resources/documentation/Developer/Base/Reference/NSObject.html

objective-C - http://en.wikipedia.org/wiki/Objective-C

Decomposition - http://www.ibm.com/developerworks/webservices/library/ar-soastyle/

Design Patterns: Elements of Reusable Object-Oriented Software - http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612

Inheritance Decomposed - http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.18.5919

Two object oriented decomposition methods, by V Rajlich 1988 - http://portal.acm.org/citation.cfm?id=339990

Decomposition/Generalization Methods for Object Oriented Programming by Vaclav Rajlich J. - http://portal.acm.org/citation.cfm?id=179804

Fundamentals of computer-aided engineering - http://www.amazon.com/Fundamentals-Computer-Aided-Engineering-Benny-Raphael/dp/0471487155

Head First Object-Oriented Analysis and Design - http://www.amazon.com/Head-First-Object-Oriented-Analysis-Design/dp/0596008678/ref=sr_1_2?s=books&ie=UTF8&qid=1287108606&sr=1-2