CSC/ECE 517 Fall 2010/ch3 S30 SK

From Expertiza_Wiki
Revision as of 00:25, 6 October 2010 by Sumi2509 (talk | contribs)
Jump to navigation Jump to search

Decomposition, message forwarding, and delegation versus inheritance in OOP

Introduction

Object oriented programming (OOP) paradigm is based upon what is known as "objects" which represent real world concepts. One of the principal advantages of object-oriented programming over procedural programming is that they enable programmers to create modules that do not need to be changed when a new type of object is added. Some basic concepts/features which are part of OOP are classes, methods, instances, encapsulation, inheritance, message forwarding, decomposition, delegation etc. This article discusses four of these concepts, namely, inheritance, message forwarding, decomposition and delegation. Specifically, comparison between inheritance and message forwarding, decomposition & delegation is made to understand when each of these can be considered as a potential alternative for inheritance, which forms core of OOP.

Elements of OOP

Some of the elements of OOP, relevant to the discussion of the topic are discussed below

Inheritance

In OOP, inheritance is a way to compartmentalize the code by creating objects which are based on previously created objects. In classical inheritance where objects are defined by classes, parent class is known as super class and the one which inherits behavior is known as sub class. Inheritance is used to relate two or more objects to each other. With the use of inheritance, methods and instance variables of the parent object are available for use in all of its child objects.

Inheritance Hierarchy

In the above example, student and teacher both inherit characters from Person object and Teaching Assistant inherits characteristics from Teacher and Student and in turn form Person.

Decomposition

Decomposition, also known as factoring is the process by which a big, complex problem is broken into smaller problems which are easier to attack, conceive and manage. Object Oriented Decomposition breaks large problem domain, based on classification entities, into smaller objects which represents some part of the problem. Object oriented decomposition works especially fine due to the concept of procedural abstraction which makes a problem easier to solve. Object-oriented decomposition results in a product that is more reusable.

For example, we could have an object referring to Drawer which supports addition of information and an object Letter which supports retrieval of content. In this case, a bigger problem domain (Drawer) refers to an object (Letter) which handles smaller problem domain. The fact that Drawer can have many letters makes decomposition relation of type "has a".

Decomposition

In the above example, Drawer object holds an instance variable which refers to Letter object. The bigger problem domain of "Drawer" has now been divided into smaller problem domains, one of which is Letter. Letter behavior is easier to maintain as compared to Drawer and any change in Drawer behavior will not affect Letter's behavior. Moreover, letter object can be part of some other big problem domain also(except of drawer).

Message Forwarding

Message Forwarding, also known as interfacing, describes the communication between objects using their public interfaces. Using message forwarding, objects can send and receive messages from each other, which help them to synchronize. Message passing is done using methods, properties and events. Message forwarding using method involves invoking an operation on an object. In response to the message the corresponding method is executed in the object and object may return some values. Message forwarding using property allows object to advertise and allow change in their state information. Events are raised by the objects in response to an internal action. Other objects can subscribe to these so that they can react to an occurrence of the event. An example for vehicles could be an 'ImpactDetected' event subscribed to by one or more 'AirBag' objects.

In some object-oriented programming languages, a message is the means to pass control to an object and if the object responds to the message, which means it has some way to handle that message. In pure object-oriented programming, message passing is performed exclusively through a dynamic dispatch strategy (process of mapping a message to a specific sequence of code at runtime.). Message forwarding techniques for communication between objects makes the interface descriptions with external systems much simpler.

Java employs message forwarding in the form of message calling. Objective C and Smalltalk heavily rely of message passing and use message sending instead of message calling.

As an example for message forwarding, when the object called "joe" (an instance of the driver class), presses the gas pedal, he literally passes an accelerate message to object "my Porsche", which in turn, invokes the "my Porsche" accelerate method.

Message Forwarding

Image taken from http://www.freecomputertutor.net/object-oriented-programming-concepts.php#messagepassing

Delegation

In OOP, two notions of delegation exist. The older usage refers to passing on the execution to some other object. While new usage refers to using method lookup rules to dispatch so-called self-calls as defined by Lieberman in his 1986 paper "Using Prototypical Objects to Implement Shared Behavior in Object-Oriented Systems".

Example for old definition:

Suppose there are class A and B defined as follows:

class A
{
 public:
   foo()
   {
     print("Object A doing the job");
   }
}
class B
{
 public:
   A a;
   foo()
   {
      a.foo();
   }
}

Here class B delegates the execution of its function foo to an object of class A.

Example of new definition:

class A
{
  foo() {
      self.bar()   // self is also known under the names "current" and "this" in other languages
  }
  bar() {
      print("a.bar")
  }
}
class B
{
  delegationlink A a
  foo() {
      a.foo() // call foo() on the a-instance
  }
  bar() {
      print("b.bar")
  }
}
a = new A()
b = new B(a)  // establish delegation between two objects

calling b.foo() will result in b.bar to be printed. Older definition would have resulted in a.bar.

Example taken from http://www.wordiq.com/definition/Delegation_(programming)

It should be noted that support for delegation as new definition is not widely supported in programming languages, though exceptions exists in the form of the languages Self and Kniesels Lava.

Delegation can be viewed as a relationship between objects where one object forwards certain method calls to another object, called its delegate. Delegation is based on dynamic binding, as it requires that a given method call can invoke different segments of code at runtime. Delegation forms the core of design patterns like wrapper design pattern.

Comparing Inheritance with Others

As discussed above, inheritance forms a basic way to compartmentalize and code reuse in object oriented programming. However in some cases, inheritance may not be the best way to approach the problem. Some of the limitations of inheritance are:

  • When single inheritance is used, an object can only inherit from a single object. If in the above given inheritance example, single inheritance is used Teaching assistant can either be student or a teacher but not both. Using multiple inheritance solves the problem (as in above example), but it can still inherit from each super class only once and hence a Teaching assistant cannot be a student at two different institutions. Also, multiple inheritance creates problem when a same trait is inherited from two different parents.
  • Inheritance hierarchy for an object is fixed at the time of instantiation and cannot be changed. In the above example, Student object cannot change to Teacher while retaining characteristics of an Employee. Hence inheritance locks the original design which becomes difficult to change.
  • When any client has access of any of the subclass, it automatically has access to the data which is part of the super class, even if data is private to the parent class. E.g in above example, access to student class gives an access of person class and can lead to unwarranted change of Person class properties.

Hence, it can be said that inheritance is not always a good approach for all the problems and some of the elements of OOP discussed above can be good alternatives for inheritance.

Decomposition and Inheritance

Traditionally, inheritance is used when there is a "is a" relationship between objects and decomposition when there is "has a" relationship between objects. An example of inheritance where "Programmer" and "Manager" extend "Employee" (is a relationship) can also be looked as, "Programmer" as a set of duties that an "Employee" may have, and this same employee may later change duties for "Manager" duties (has a relationship). Hence the way we design our system makes decomposition or inheritance favorable. But as a rule of thumb, object oriented design based on decomposition is more stable when parent objects are likely to change. In our decomposition example considered above, even if drawer functionality and role change drastically basic participants (letter object in this case) are likely to remain same and can be used in some other domain also. Hence, decomposition is used when there is lot of code reuse and/or polymorphism. By separating behaviors from the primary class hierarchy and including specific behavior for the child class, child class is not restricted by behavior of its ancestral classes. Inheritance should be preferred when there is significant interaction between parent and its child classes. If parent must call back to child methods, then inheritance is usually the good answer. As an example, consider a class apple which inherits from its super class Fruit. Any change in the behavior of fruit will directly effect Orange which "is a" type of fruit. When super class and its subclasses are meant to be tied together inheritance should be used as it allows us to take advantage of dynamic binding and polymorphism.

Message Passing and Inheritance

Object oriented programs generally have two kinds of structures; one style is to have an inheritance hierarchy. The other is to have a pattern of message passing. Inheritance structure represents how different objects are related by their types. For example, in the program that models water use, Faucets and WaterPipes can be modeled as same kind of objects, except that Faucets can be turned on and off and WaterPipes can have multiple connections to other WaterPipes. Thus Faucet and WaterPipe objects can inherit from a common antecedent with all common traits and will have their specific properties in their classes. Message passing structure represents how program works. For example, in the above mentioned program for water use, Appliance objects might send messages requesting water to Valves, and Valves to WaterPipes. The Building object can communicate through messages with all the Valves, Faucets, and WaterPipes, but not directly with Appliances. The connection between different objects which know whom to contact and pass message defines program structure. [Example taken from: http://objc.toodarkpark.net/oop.html ]

There are situations where inheritance structure for the program may not work well. For example, consider an object Room which defines characteristics for the room. Room can be cubiod in shape so Object Room can inherit characteristics from Cuboid Object. However problem arise when different Rooms can have different shapes say cylindrical. Inheriting from both Cubiod and Cylindrical Objects will make Room both cylindrical and cubiod, instead of being cylindrical or cubiod in shape. Hence inheritance would not work here. A better way would be to have separate objects Shape and Room and shape object points to Cubiod or Cylindrical object. A Room object can interact with object Shape by passing messages. In this design approach access to all of other object facilities is not granted, instead an object needs to pass messages to access attributes and operations of the other class. Thus message passing exposes only the required interface of the object.

Delegation and Inheritance

Delegation is closely related to message forwarding and as discussed in above section, just like message forwarding and inheritance, there can be two kinds of structures for programs involving inheritance and delegation. Delegation supports the prototype-based programming model and inheritance, class based programming model. Inheritance schemes involve two kinds of objects, classes and instances. A class defines what is known as structure of its instances. Each instance maintains its values of attributes defined in the structure and the state of an object is given by the values of its attributes at any point in time. In delegation, there is only one type of object which represents real-world items. These objects are often taken to be “instances without classes.” This independence of all objects in delegation scheme allows delegation to capture the behavior of inheritance to some extent.

When it comes to building large systems with many objects, using an inheritance based system seems to make more sense. This is due to the fact that inheritance based system forces programmer to think and place some common behavior in super classes. In delegation based system, common behavior can be put in a single class, but this results in dependencies between objects and changing one object might result in errors in another object which is delegating the job to the first object.

In the nutshell, following are the situations when using delegations over inheritance is beneficial:

  • When an object can play more than one role in the system, then sub-classing is not advisable, as the numbers of combinations are very large. Delegation would result in fewer classes and work best in this case.
  • Inheritance, as discussed before, is a static relationship. If a class inherits from another class, this relationship cannot be changed at run time. On the other hand, Delegation is a dynamic relationship that can be changed at runtime.
  • If an object needs to hide a function or an attribute from its super-class, then it is not a right candidate for inheritance. Delegation is an appropriate solution for this situation.
  • If there’s no control over the class which is being inherited, it is good to use delegation over inheritance as inheritance has the risk that classes may have to be changed due to changes in parent class over which there’s no control.
  • Delegation, unlike Inheritance, doesn’t force an object to accept all the methods of the super class, an object can only provide those methods that really make sense.

However, delegation has certain disadvantages over inheritance

  • Delegation results in large number of objects in memory, which could result in performance issue when the objects in memory grow fast due to inefficient handling or disposal of objects.
  • Delegation results in code more difficult to read understand and debug. This is due to the fact that delegation involves certain level of object indirection.

Conclusion

OOP has many features which makes it a favorable option in many problem domains. However, based on the requirements of the application, what is needed are subtle design decisions about which feature of OOP is a best option. Inheritance based models seem a very attractive choice for all object oriented applications, which is not true. As discussed in this article, techniques like decomposition, delegation and message forwarding do compass better alternatives for inheritance for some object oriented applications. What is needed is a perspective to see which OOP concept fits best in the overall picture.