CSC/ECE 517 Fall 2010/ch1 25 ag: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
Line 179: Line 179:
  class TrianglePainter : Painter
  class TrianglePainter : Painter
  {
  {
     void draw(Triangel t) { }
     void draw(Triangle t) { }
  }
  }
</td>
</td>
</tr>
</tr>
</table>
</table>
Now we need a canvas painter, which should have the capability to draw any of the above shape. In C++, this is pretty straight forward, you define a new class which extends all painters.
class CanvasPainter : SquarePainter, CirclePainter, TrianglePainter
{
    // ..
}
How can do the same in Java? Ofcourse, we will use composition, but how will you know which version of draw function to call? May be we can write some thing like this:
if (shape instanceof Triangle)
{
      trianglePainter.draw((Triangle) shape);
}
else if(shape instanceof Circle)
{
      circlePainter.draw((Circle) shape);
}
Unfortunately this is not a scalable solution and for every new tool we add to our paint program, we will have to change this code. This is where design patterns like [http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern Chain of Responsibility] proves helpful.


= Conclusion =
= Conclusion =

Revision as of 16:38, 20 September 2010


What is Inheritance?

Inheritance is one of important attribute of Object Oriented Languages. Using Inheritance a class can reuse code defined in another class. The advantage of this approach is that, we can have an hierarchy of Inheritance and write all generic code in the parent class and add only specific functions in the child classes.

Example:

class ElectricAppliance
{
    public void on() { ... }
    public void off() { ... }
}
class TempControlAppliance : public ElectricAppliance
{
    public void increaseTemp() { .. }
    public void decreaseTemp() { .. }
}
class Heater : public TempControlAppliance 
{
    public void heat() { .. }
}
class Cooler : public TempControlAppliance 
{
    public void cool() { .. }
}

If we don't have inheritance, we might have to duplicate all the functions in both Heater and Cooler class. Inheritance is very important to achieve DRY (Don't Repeat Yourself). In the above class hierarchy, TempControlAppliance is super (parent) class for both Cooler and Heater subclass (child).

What is Multiple Inheritance

When a class inherits from more than one class i.e., a child class having more than one parent class, is called multiple inheritance. Though this feature offers more flexibility, it seem to overwhelm most of the developer, so some of the object oriented language designer decided not to support Multiple Inheritance. Java and C# are some of the most famous language which don't support multiple inheritance. We need careful design to take proper advantage of Multiple inheritance, otherwise you might end up in spaghetti code, with classes inheritancing from multiple classes.

Example: Building upon the above example, let us say we have a new device called Air Conditioner which can both heat and cool air. We might have a new class called AirConditioner as follows:

class AirConditioner : public Heater, public Cooler
{
     public void setThresholdTemperature(int temp) { ... }
}

Good and bad of Multiple Inheritance

Good:

  • Multiple Inheritance strongly supports DRY principle, which leads to better software which is extensible, cohesive, less coupled and modular.
  • Multiple Inheritance is very common in real world, in which object takes attributes from different objects.
  • Multiple Inheritance is more expressive than Single Inheritane, so it gives flexibility in designing software with complex object relationships.

Bad:

  • To take complete advantage of Multiple Inheritance needs a thoughtful design. Without a good design, Multiple Inheritance infact might lead to bad code.
  • Multiple Inheritance is overwhelming to developers. Requires more experience to properly use Multiple Inheritance.
  • Multiple Inheritance is hard to use and make the language complicated. Some example like what happens when you Inherit from multiple classes which has same method?

Problems with Multiple Inheritance

There are some common problems with Multiple Inheritance. One of the most common problem with Multiple Inheritance is called Diamond Problem Let us try to understand this problem through an example.

We are modelling a Car. We know that we have a Mechanical Car and a Electrical Car.

class Car
{
    public:
       void start() { cout << "Start with loud noise";
       void steer();
}
class MechCar : public Car
{
}
class ElectricCar : public Car
{
    public:
      void start() { cout << "start without noise";
}

After few year, we have Hybrid Cars in the market and we have to model a Hybrid Car now. This how we could do it in C++.

class HybridCar : public MechCar, ElectricCar
{
     // This has two copies of start()
}

From the above we can see that there is an ambiguity about which version of start function to use. C++ solves this problem through Virtual Inheritance. Below is how you fix the problem.

HybridCar h;
h.ElectricCar::start();

There are also ambiguities during the type casting. For more details about Virtual Inheritance refer here

Multiple Inheritance - Do we really need this?

Many languages which don't support multiple inheritance completely, has some ways of emulating multiple inheritance. We will consider Java as an example which does not support Multiple Inheritance completely and see how we can emulate multiple inheritance in Java. It is not completely true to say that Java does not support multiple inheritance, in fact with interfaces we can have multiple inheritance. Since, Interface just defines the behaviour, it does not have any attributes to describe it, Interfaces are not considered as a Class.

Let us consider the previous example of an AirConditioner:

class AirConditioner extends [______________]

We cannot fit both the classes there since a class in Java can extend from a single class. But Java classes can implement multiple interfaces.

public interface IHeater {
   void heat();
}
public interface ICooler {
   void cool();
}

public Heater extends TempControllerAppliance implements IHeater {
   public void heat() { .. }
}

public Cooler extends TempControllerAppliance implements ICooler{
   public void cool() { .. }
}

public class AirConditioner extends TempControllerAppliance implements IHeater, ICooler, IAirConditioner {
   private ICooler cooler = new Cooler();
   private IHeater heater = new Heater();

   public void cool() {
       cooler.cool();
   }
 
   public void heat() {
       heater.heat();
   }  
   
   public void setThreshold(int t) { ... }   
}

To implement multiple inheritance in Java, we need to the following: 1. Make every class implement an Interface 2. Classes extends these interfaces to add logic 3. When a class needs to extend from Multiple interfaces, you can implement those interfaces and delegate the call to the actual implementation. This is achieved through Composition.

We can see that it is possible to emulate multiple inheritance through composition.

Let us take another example and see how we can emulate Multiple inheritance in Java. We are developing a paint application. We have separate tools to draw each primitive shapes.

class Shape
{
   //...
}
class Square : Shape
{
  // ..
}
class Circle : Shape
{
   //..
}
class Triangle : Shape
{
   //..
}
class Painter
{
    //..
}
class SquarePainter : Painter
{
    void draw(Square s) { }
}
class CirclePainter : Painter
{
   void draw(Circle c) { }
}
class TrianglePainter : Painter
{
   void draw(Triangle t) { }
}

Now we need a canvas painter, which should have the capability to draw any of the above shape. In C++, this is pretty straight forward, you define a new class which extends all painters.

class CanvasPainter : SquarePainter, CirclePainter, TrianglePainter
{
    // ..
}

How can do the same in Java? Ofcourse, we will use composition, but how will you know which version of draw function to call? May be we can write some thing like this:

if (shape instanceof Triangle)
{
      trianglePainter.draw((Triangle) shape);
}
else if(shape instanceof Circle)
{
      circlePainter.draw((Circle) shape);
}

Unfortunately this is not a scalable solution and for every new tool we add to our paint program, we will have to change this code. This is where design patterns like Chain of Responsibility proves helpful.

Conclusion

Table here

Further Reading

Reference