CSC/ECE 517 Fall 2012/ch1 1w14 gv: Difference between revisions
m (Created page with "1w14: Advantages and disadvantages of multiple inheritance Multiple inheritance has been proposed as a way to avoid reimplementation of classes that need to include functiona...") |
|||
(72 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
= Multiple Inheritance = | |||
Multiple inheritance has been | [http://en.wikipedia.org/wiki/Multiple_inheritance Multiple Inheritance] is one of the features of a few [http://en.wikipedia.org/wiki/Object-oriented_programming 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. | |||
__TOC__ | |||
= 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 [http://en.wikipedia.org/wiki/Do_not_repeat_yourself 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 [http://en.wikipedia.org/wiki/C%2B%2B C++], [http://en.wikipedia.org/wiki/Eiffel_%28programming_language%29 Eiffel], Perl, Python etc. On the other hand, some object oriented languages like [http://en.wikipedia.org/wiki/C_Sharp_%28programming_language%29 C#], [http://en.wikipedia.org/wiki/Small_talk Smalltalk], Java, [http://en.wikipedia.org/wiki/Modula_3 Modula-3], [http://en.wikipedia.org/wiki/Objective-C 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. | |||
[[File:1.png|thumb|center|800px|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<ref>http://tinyurl.com/8ehk6bd</ref> of multiple inheritance are desirable, all of which are supported : | |||
* Multiple specialization: An instance of a class that is derived by multiple inheritance can be conceptually thought of as a specialization of two different classes. For example, a class InputOutputStream is a specialization of both an InputStream and OutputStream. | |||
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. | |||
* Multiple inheritance also helps in modeling relationships that are multi way. Without MI, a choice has to be made among the many possible relationships, even though all of them are valid. When a class needs properties found in more than one base class, MI might be useful. | |||
* Multiple inheritance might lead to a higher possibility of code reuse. Every additional base class enhances code reuse. Without MI, we would be forced to use the has-a relationship, thereby reducing code reuse factor (increases implementation and testing time). | |||
= 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: | |||
* Implicit resolution with arbitrary rules such as the preorder traversal of an inheritance tree as in Python | |||
* Explicit resolution by the programmer | |||
* No resolution i.e. disallowing methods with same names altogether | |||
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: | |||
* There could be only one shared copy of the class inherited multiple times. | |||
* There could always be one copy for each time a class is inherited (sometimes, having a base class explicitly duplicated might be a right design decision). | |||
* The decision could be left to the programmer( as in C++ ). | |||
[http://en.wikipedia.org/wiki/Virtual_inheritance 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 [http://en.wikipedia.org/wiki/Diamond_problem#The_diamond_problem 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: | |||
[[File:2.png|thumb|center|350px|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 <ref>http://en.wikipedia.org/wiki/Yo-yo_problem</ref> 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: | |||
[http://en.wikipedia.org/wiki/Is-a "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. | |||
[http://en.wikipedia.org/wiki/Has-a "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<ref>http://msdn.microsoft.com/en-us/library/27db6csx%28v=vs.80%29.aspx</ref> shows examples of both "is a" and "has a" relationships: | |||
[[File:3.png|thumb|center|500px|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<ref>http://adhirajrankhambe.wordpress.com/2009/01/04/a-word-about-modules-in-ruby/</ref> 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<ref>http://www.tutorialspoint.com/ruby/ruby_modules.htm</ref> to define a module: | |||
<pre> | |||
module <Module_Name> | |||
//constants | |||
//methods | |||
end | |||
</pre> | |||
Methods and constants in modules are declared just like they are declared in class. | |||
Another use of modules is mixins. | |||
==Mixin inheritance== | |||
A [http://en.wikipedia.org/wiki/Mixin 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)<ref>http://www.ruby-doc.org/docs/ruby-doc-bundle/UsersGuide/rg/modules.html</ref>. Mixins have a clear advantage in terms of [http://en.wikipedia.org/wiki/Modularity_%28programming%29 modularity] and [http://en.wikipedia.org/wiki/Reusability 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 [http://en.wikipedia.org/wiki/Java_script JavaScript] do not support mixins but can copy methods from one object to other at run time thereby emulating mixins. | |||
Following example<ref>http://www.tutorialspoint.com/ruby/ruby_modules.htm</ref> shows how to use mixins in a class: | |||
<pre> | |||
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 | |||
</pre> | |||
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. | |||
<pre> | |||
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) | |||
{ | |||
} | |||
}; | |||
</pre> | |||
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<ref>http://docs.python.org/tutorial/classes.html#multiple-inheritance</ref> demonstrates multiple inheritance in Python: | |||
<pre> | |||
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" | |||
</pre> | |||
In Python, parent method does not automatically get called. If methods are overriden, the parent version of method should be called. The keyword "[http://chandlerproject.org/Projects/UsingSuper 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:<br> | |||
[http://expertiza.csc.ncsu.edu/wiki/index.php/CSC/ECE_517_Fall_2012/ch1_1w14_gv#Mixin_inheritance 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. | |||
<pre> | |||
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 | |||
} | |||
} | |||
</pre> | |||
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 = | |||
<references/> | |||
= See Also = | |||
*http://www.javaworld.com/javaqa/2002-07/02-qa-0719-multinheritance.html<br> | |||
*http://java.sun.com/docs/white/langenv/Simple.doc2.html#4076<br> | |||
*http://marcricblog.blogspot.com/2007/11/multiple-inheritance-mixins-and-blog_20.html<br> | |||
*http://adhirajrankhambe.wordpress.com/2009/01/04/a-word-about-modules-in-ruby/<br> | |||
*http://blogs.msdn.com/b/ericgu/archive/2006/07/24/677225.aspx<br> | |||
*http://stackoverflow.com/questions/225929/what-is-the-exact-problem-with-multiple-inheritance<br> |
Latest revision as of 01:43, 21 September 2012
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.
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.
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<ref>http://tinyurl.com/8ehk6bd</ref> of multiple inheritance are desirable, all of which are supported :
- Multiple specialization: An instance of a class that is derived by multiple inheritance can be conceptually thought of as a specialization of two different classes. For example, a class InputOutputStream is a specialization of both an InputStream and OutputStream.
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.
- Multiple inheritance also helps in modeling relationships that are multi way. Without MI, a choice has to be made among the many possible relationships, even though all of them are valid. When a class needs properties found in more than one base class, MI might be useful.
- Multiple inheritance might lead to a higher possibility of code reuse. Every additional base class enhances code reuse. Without MI, we would be forced to use the has-a relationship, thereby reducing code reuse factor (increases implementation and testing time).
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:
- Implicit resolution with arbitrary rules such as the preorder traversal of an inheritance tree as in Python
- Explicit resolution by the programmer
- No resolution i.e. disallowing methods with same names altogether
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:
- There could be only one shared copy of the class inherited multiple times.
- There could always be one copy for each time a class is inherited (sometimes, having a base class explicitly duplicated might be a right design decision).
- The decision could be left to the programmer( as in C++ ).
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:
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 <ref>http://en.wikipedia.org/wiki/Yo-yo_problem</ref> 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<ref>http://msdn.microsoft.com/en-us/library/27db6csx%28v=vs.80%29.aspx</ref> shows examples of both "is a" and "has a" relationships:
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<ref>http://adhirajrankhambe.wordpress.com/2009/01/04/a-word-about-modules-in-ruby/</ref> 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<ref>http://www.tutorialspoint.com/ruby/ruby_modules.htm</ref> 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)<ref>http://www.ruby-doc.org/docs/ruby-doc-bundle/UsersGuide/rg/modules.html</ref>. 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<ref>http://www.tutorialspoint.com/ruby/ruby_modules.htm</ref> 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<ref>http://docs.python.org/tutorial/classes.html#multiple-inheritance</ref> 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
<references/>
See Also
- http://www.javaworld.com/javaqa/2002-07/02-qa-0719-multinheritance.html
- http://java.sun.com/docs/white/langenv/Simple.doc2.html#4076
- http://marcricblog.blogspot.com/2007/11/multiple-inheritance-mixins-and-blog_20.html
- http://adhirajrankhambe.wordpress.com/2009/01/04/a-word-about-modules-in-ruby/
- http://blogs.msdn.com/b/ericgu/archive/2006/07/24/677225.aspx
- http://stackoverflow.com/questions/225929/what-is-the-exact-problem-with-multiple-inheritance