CSC/ECE 517 Fall 2012/ch1 1w14 gv

From PG_Wiki
Jump to: navigation, search

Multiple Inheritance

Multiple Inheritance is one of the features of a few Object-oriented (OO) languages in which a class inherits properties (variables & methods) from more than one parent class . The former is called a sub or child class and the latter is called a super class or ancestor class.

We shall discuss the concept of Multiple inheritance, advantages & disadvantages of using multiple inheritance and how it is achieved in different Object Oriented languages.

Contents


Introduction

Multiple Inheritance extends the concept of single inheritance (where one sub class inherits the properties from one super class) to a paradigm where one child class will inherit properties from multiple parent classes. In accordance to the DRY(Donot Repeat Yourself) principle, multiple inheritance lets us avoid declaring/defining the same properties/methods in different classes.

For example, consider a class to describe teaching assistants. A TA class will draw a lot of properties from both Student and University employee classes. So, instead of implementing them again, we can programmatically specify that class TGradTeachingAsst extends both Student and Employee classes

This feature is supported by several object oriented languages such as C++, Eiffel, Perl, Python etc. On the other hand, some object oriented languages like C#, Smalltalk, Java, Modula-3, Objective C etc. do not support multiple inheritance. Multiple inheritance is still a debatable feature in traditional object-oriented languages, as it has been omitted from languages like Java etc. Nonetheless, users of these languages often complain about having to work around the absence of multiple inheritance. Multiple inheritance, if used wisely could be advantageous. On the other hand, if the developers are not smart enough to use it, it could lead to a lot of complexities.

Illustration for Multiple Inheritance

Advantages

Multiple inheritance is widely considered a useful feature as it is commonly used in the above listed OO languages. It is also a frequently requested feature in languages that do not provide it. Programmers often devise their own workaround procedures to achieve what multiple inheritance offers. One of the advantages of multiple inheritance is that it obviates improvised solutions that would otherwise be fairly obvious.

Following uses[1] of multiple inheritance are desirable, all of which are supported :

In general, multiple inheritance allows us to combine classes to create new abstractions, and these new abstractions are of more value than the individual abstractions by themselves.

Drawbacks of multiple inheritance

Even though multiple inheritance is popular(among the programming communities), its appeal is not universal, as it was excluded from other prominent languages like Java. This section discusses few other drawbacks of using multiple inheritance:

Name resolution

A common problem in implementing multiple inheritance is to resolve conflicts between methods inherited from multiple super classes that have the same signature. Those methods, despite having the same signature, may not refer to the same conceptual operation. Languages can overcome this issue in three ways:

Eiffel’s solution of having the programmer rename methods to resolve name conflicts makes resolving such a problem easier on the programmer. However, a similar feature was considered for inclusion into C++, and later rejected as it can lead to following a convoluted trail of chained aliases.

Repeated inheritance

Another issue that must deal with when implementing multiple inheritance is repeated inheritance, which arises when a class indirectly inherits from another class multiple times. This can be dealt with in three ways:

Virtual inheritance doesn’t include copies of instance variables, which thus saves space and prevents accidental modification to the wrong set of variables. Initially, if programmers overlook a case of repeated inheritance, unexpected side effects may occur. For example, when a class operation performs a depth-first traversal of the inheritance graph, the same class’ method may get called twice.

The problem caused by repeated inheritance is also termed as the diamond problem (also referred to as the "deadly diamond of death"). This refers to the method invocation ambiguity that arises when two classes B and C inherit from A, and a fourth class D inherits from both B and C. If D invokes a method defined in A but is not overridden locally, and B and C have their own implementations of that method, then the method to invoke is unclear

For example consider the following figure:

Error creating thumbnail: /var/www/html/wiki/bin/ulimit4.sh: line 4: /usr/bin/convert: No such file or directory
Double Diamond Problem in Multiple Inheritance


When we invoke analyze() from Graph_Analyzer, as it doesn’t have its own overridden version of analyze, it is unclear if the invoked version of analyze will be Cache_Analyzer.analyze() or Loop_Analyzer.analyze().

Misuse

Another problem with multiple inheritance is that it is often overused; i.e., some programmers use it in an unclear or undesirable manner. It is often considered one of the deadly programmer sins. Programmers often use multiple inheritance to model “HAS-A” relationships, even though they are supposed to use it only when the “IS-A” relationship is valid

Obscurity

Another significant problem with most implementations is the potential for obscure code. Take, for example, a class A that inherits from classes B and C, which both define a method foo(). When running code in C on behalf of A, and C calls foo(), in many languages, A’s foo() method will be used, which could be the method found in B, and not the one found in C. When such a thing happens, it is certainly not obvious at all when looking at C’s code. Multiple inheritance generally adds a lot of complexity to an object oriented system, for both the language designer and the end user, and thus is potentially easy to misuse.

The Yo-Yo problem and Hrair’s limit:

The yo-yo problem [2] explains the situation where a programmer has to understand a convoluted inheritance graph that is so long and complicated that the programmer has to constantly switch between many different class definitions to follow the program. It often happens when there is a overuse of multiple inheritance. Hrair’s limit says that humans can, on an average, process at most approximately seven objects/concepts. So multiple inheritance inadvertently adds to the complexity of design.

Usage guide lines

Inheritance is a useful programming concept, but it is easy to use inappropriately. Often interfaces do the job better. There are two ways to depict class relationships in object-oriented languages: "IS-A" relationship: The derived class is clearly a kind of the base class. Objects in an inheritance hierarchy should have an "is a" relationship with their base class because they inherit the fields, properties, methods, and events defined in the base class. "HAS-A" relationship: A relationship where one object “belongs to” or contains another object but is not visibly a kind of that object. Inheritance is inappropriate in this situation and if misused, it might inherit properties that make no sense to the child class

The following figure[3] shows examples of both "is a" and "has a" relationships:

Error creating thumbnail: /var/www/html/wiki/bin/ulimit4.sh: line 4: /usr/bin/convert: No such file or directory
IS-A and HAS-A Rule examples

Alternatives

Because of the above discussed drawbacks, few languages have implemented concepts like Modules and Mixins to emulate the power of multiple inheritance.

Modules

A module is a collection of methods and constants. Modules can have no sub classes and instances. One of the major uses of modules is to encapsulate the methods and constants in one location. Modules provide namespace[4] and also prevent name clashes. Ruby's built in modules like Math exhibit these features. For example, Math::E gives the value of e(base of natural logarithms). The '::' operator tells the ruby interpreter which module it should look up for the value of E. Syntax[5] to define a module:

module <Module_Name>
     //constants
     //methods
end

Methods and constants in modules are declared just like they are declared in class. Another use of modules is mixins.

Mixin inheritance

A Mixin is a class that encapsulates additional attributes or properties which can be used by other classes. Mixins are typically narrow in scope (include only a few properties) and a class might inherit several mixins (for several properties)[6]. Mixins have a clear advantage in terms of modularity and reusability. Mixins cannot be instantiated independently (cannot stand on their own) and are not instantiated directly, as they might depend on the presence of a particular interface in the classes that inherit them. Generally, mixins are self contained. Multiple Inheritance and Mixins differ in semantics. A class which uses multiple inheritance might utilize a mixin as a part of that multiple inheritance. Mixins can be thought of as an interface that is already implemented. We "include" a module in a class definition, its methods are effectively appended, or "mixed in" to the class. Languages like C#, Perl, Python, Ruby take advantage of mixins. Languages like JavaScript do not support mixins but can copy methods from one object to other at run time thereby emulating mixins. Following example[7] shows how to use mixins in a class:

module A
   def a1
	puts “Method a1”
   end
   def a2
	puts “Method a2”
   end
end
module B
   def b1
	puts “Method b1”
   end
   def b2
	puts “Method b2”
   end
end
module C
   def c1
	puts “Method c1”
   end
   def c2
	puts “Method c2”
   end
end
class Example
include A
include B
   def e1
   end
end
exp = Example.new   // Creating a new object of Example class
exp.a1		     // Calling a1 method of module A
exp.a2		     // Calling a2 method of module A
exp.b1		     // Calling b1 method of module B
exp.b2		     // Calling b2 method of module B
exp.c1		     // Calling c1 method of module C
exp.c2		     // Calling c2 method of module C
exp.e1		     // Calling e1 method defined inside class Example

Each module A, B and C have two methods a1, a2, b1, b2 and c1, c2 respectively. "include" is the keyword used in Ruby to embed a module in a class. The class Example includes modules A, B and C. Thus, an object of the class Example can access all the methods in A, B and C modules. We can see that Example class shows multiple inheritance or a mixin.

Comparison between C++/Java/Python/Ruby

Multiple Inheritance is supported by several object oriented languages such as C++, Ruby, Perl, Python etc. Let us compare how multiple inheritance is implemented in each of these languages.

C++

In C++, multiple inheritance is achieved by using the colon (:) operator to extend different base classes.

class Person
{
private:
    std::string m_strName;
    int m_nAge;
 public:
    Person(std::string strName, int nAge,
        : m_strName(strName), m_nAge(nAge)
    {
    }
    std::string GetName() 
    { 
    	return m_strName; 
    }
    int GetAge() 
    { 
    	return m_nAge; 
    }
    bool IsMale() 
    { 
    	return m_bIsMale; 
    }
};
class Employee
{
private:
    std::string m_strEmployer;
    double m_dWage;
public:
    Employee(std::string strEmployer, double dWage)
        : m_strEmployer(strEmployer), m_dWage(dWage)
    {
    }
    std::string GetEmployer() 
    { 
     	return m_strEmployer; 
    }
    double GetWage() 
    { 
    	return m_dWage; 
    }
};
 
// Teacher publicly inherits Person and Employee

class Teacher: public Person, public Employee
{
private:
     int m_nTeachesGrade;
 
public:
    Teacher(std::string strName, int nAge, std::string strEmployer, double dWage, int nTeachesGrade)
        : Person(strName, nAge), Employee(strEmployer, dWage), m_nTeachesGrade(nTeachesGrade)
    {
    }
};

In the above example, the "Teacher" class extends the properties of both Person and Employee classes. Thus, an object of "Teacher" class can use the public methods of both the "Person" and "Employee "classes.

Python

Python supports a limited form of multiple inheritance. The parent classes are included within parenthesis following the class name. The following example[8] demonstrates multiple inheritance in Python:

class First(object):
    def __init__(self):
        print "first"

class Second(First):
    def __init__(self):
        print "second"

class Third(First):
    def __init__(self):
        print "third"

class Fourth(Second, Third):
    def __init__(self):
        super(Fourth, self).__init__()
        print "that's it"

In Python, parent method does not automatically get called. If methods are overriden, the parent version of method should be called. The keyword "super" is used to call the parent's method. In the above Python example, class "Fourth" inherits classes "Second" and "Third" . The order in which classes are called in the above example is Fourth, Second, Third, First.

Ruby

Multiple Inheritance is not supported by Ruby. It supports Single inheritance and a concept called "Mixins" which emulates multiple inheritance. The following example demonstrates the concept of Mixins:
Implementing multiple inheritance using Mixins

Java

Java, another OO language, does not support multiple inheritance. But interfaces in Java can be used to emulate multiple inheritance. Java uses the "extends" keyword to inherit a base class and the keyword "implements" for interfaces.

interface I1 {
  abstract void test(int i);
}
interface I2 {
  abstract void test(String s);
}
public class C1 {
  public void test(float f) {
     float f = this.f;
  }
}
public class MultInterfaces extends C1 implements I1, I2 {
  public void test(int i) {
    System.out.println("In MultInterfaces.I1.test");
  }
  public void test(String s) {
    System.out.println("In MultInterfaces.I2.test");
  }
  public static void main(String[] a) {
    MultInterfaces t = new MultInterfaces();
    t.test(42);		// calls the implemented interface I1 method
    t.test("Hello");	// calls the implemented interface I2 method
    t.test(1.42);	// calls the class C1 method
  }
}

In Java, a class cannot extend two classes, but can implement more than 1 interfaces. The methods in the interfaces should be implemented by the class using them. In the above example C1 is a parent class, I1 and I2 are interfaces with abstract methods. Objects of the MultInterfaces class can be created only if it implements abstract methods from interfaces I1 and I2.

Conclusion

We have discussed about Multiple Inheritance, pros and cons of using Multiple Inheritance and some alternatives for it. Multiple Inheritance would definitely offer better options to designers and developers using OO languages. Also, languages which don't support MI have other alternatives like using Mixins to simulate Multiple Inheritance.

References

  1. http://tinyurl.com/8ehk6bd
  2. http://en.wikipedia.org/wiki/Yo-yo_problem
  3. http://msdn.microsoft.com/en-us/library/27db6csx%28v=vs.80%29.aspx
  4. http://adhirajrankhambe.wordpress.com/2009/01/04/a-word-about-modules-in-ruby/
  5. http://www.tutorialspoint.com/ruby/ruby_modules.htm
  6. http://www.ruby-doc.org/docs/ruby-doc-bundle/UsersGuide/rg/modules.html
  7. http://www.tutorialspoint.com/ruby/ruby_modules.htm
  8. http://docs.python.org/tutorial/classes.html#multiple-inheritance

See Also

Personal tools
Namespaces
Variants
Actions
Navigation
Toolbox