CSC/ECE 517 Fall 2010/ch2 2f EC: Difference between revisions
No edit summary |
No edit summary |
||
(3 intermediate revisions by the same user not shown) | |||
Line 20: | Line 20: | ||
<br> | <br> | ||
Figure 1: Simple case of inheritance | <i>Figure 1: Simple case of inheritance</i> | ||
<br> | <br> | ||
Line 27: | Line 27: | ||
The use of inheritance is convenient because we did not have to duplicate code to create a new subclass and increases code re-usability<sup>1</sup>. Also, if changes are made to the superclass, those changes will be reflected in the subclasses as well. | The use of inheritance is convenient because we did not have to duplicate code to create a new subclass and increases code re-usability<sup>1</sup>. Also, if changes are made to the superclass, those changes will be reflected in the subclasses as well. | ||
</p> | </p> | ||
<br> | |||
<h2>Single Inheritance</h2> | <h2>Single Inheritance</h2> | ||
Line 43: | Line 44: | ||
<code> | <code> | ||
public class Bike { <br> | public class Bike { <br> | ||
// Implementation of generic bike class <br> | |||
// ie. frame, wheels<br> | |||
}<br> | }<br> | ||
<br> | <br> | ||
public class MountainBike extends Bike {<br> | public class MountainBike extends Bike {<br> | ||
// inherit generic bike components<br> | |||
// add shocks, fat tires, etc.<br> | |||
}<br> | }<br> | ||
<br> | <br> | ||
public class RoadBike extends Bike {<br> | public class RoadBike extends Bike {<br> | ||
// inherit generic bike components<br> | |||
// add narrow tires, drop-style handlebars, etc.<br> | |||
}<br> | }<br> | ||
</code> | </code> | ||
</p> | </p> | ||
<br> | |||
<h2>Multiple Inheritance</h2> | <h2>Multiple Inheritance</h2> | ||
Line 66: | Line 68: | ||
[[Image:EC_fig2.jpg]] | [[Image:EC_fig2.jpg]] | ||
<br> | <br> | ||
Figure 2: Case of multiple inheritance | <i>Figure 2: Case of multiple inheritance</i> | ||
<br> | <br> | ||
<br> | <br> | ||
Line 72: | Line 74: | ||
<br> | <br> | ||
<br> | <br> | ||
Languages that support multiple inheritance include C++, Python, Perl, Lisp, | Languages that support multiple inheritance include C++, Python, Perl, Lisp, Tcl<sup>2</sup>, and Ruby (with the use of mixins). | ||
<br> | <br> | ||
<br> | <br> | ||
Line 80: | Line 82: | ||
<code> | <code> | ||
class Frame {<br> | class Frame {<br> | ||
// frame of a bike<br> | |||
}<br> | }<br> | ||
<br> | <br> | ||
class Wheels {<br> | class Wheels {<br> | ||
// wheels of a bike<br> | |||
}<br> | }<br> | ||
<br> | <br> | ||
class Bike: public Frame, public Wheels<br> | class Bike: public Frame, public Wheels<br> | ||
// create bike by putting together frame and wheels<br> | |||
}<br> | }<br> | ||
<br> | <br> | ||
</code> | </code> | ||
</p> | </p> | ||
<br> | |||
<h1>Advantages of Multiple Inheritance</h1> | <h1>Advantages of Multiple Inheritance</h1> | ||
Line 134: | Line 137: | ||
[[Image:EC_fig3.jpg]] | [[Image:EC_fig3.jpg]] | ||
<br> | <br> | ||
Figure 3: Multiple inheritance allows more flexible hierarchies. | <i>Figure 3: Multiple inheritance allows more flexible hierarchies.</i> | ||
<br> | <br> | ||
<br> | <br> | ||
Line 142: | Line 145: | ||
[[Image:EC_fig4.jpg]] | [[Image:EC_fig4.jpg]] | ||
<br> | <br> | ||
Figure 4: Single inheritance limits hierarchy. | <i>Figure 4: Single inheritance limits hierarchy.</i> | ||
<br> | <br> | ||
<br> | <br> | ||
Line 150: | Line 153: | ||
Therefore, as the example demonstrates the structure for single inheritance is more constrained. | Therefore, as the example demonstrates the structure for single inheritance is more constrained. | ||
</p> | </p> | ||
<br> | |||
<h1>Disadvantages of Multiple Inheritance</h1> | <h1>Disadvantages of Multiple Inheritance</h1> | ||
Line 161: | Line 165: | ||
Some key issues include name and method collision<sup>3</sup>, the diamond problem<sup>4</sup>, and conceptual difficulties and understandability problems<sup>3</sup>. | Some key issues include name and method collision<sup>3</sup>, the diamond problem<sup>4</sup>, and conceptual difficulties and understandability problems<sup>3</sup>. | ||
</p> | </p> | ||
<br> | |||
<h2>Name collision</h2> | <h2>Name collision</h2> | ||
<p> | <p> | ||
Line 168: | Line 173: | ||
[[Image:EC_fig5.jpg]] | [[Image:EC_fig5.jpg]] | ||
<br> | <br> | ||
Figure 5: Which x value will print_x() method print? | <i>Figure 5: Which x value will print_x() method print?</i> | ||
<br> | <br> | ||
<br> | <br> | ||
It is unclear which value of 'x' would be printed. Different languages and compilers will handle this differently. | It is unclear which value of 'x' would be printed. Different languages and compilers will handle this differently. | ||
</p> | </p> | ||
<br> | |||
<h2>Method collision</h2> | <h2>Method collision</h2> | ||
<p> | <p> | ||
Line 180: | Line 186: | ||
[[Image:EC_fig6.jpg]] | [[Image:EC_fig6.jpg]] | ||
<br> | <br> | ||
Figure 6: Which show() method will Package inherit? | <i>Figure 6: Which show() method will Package inherit?</i> | ||
</p> | </p> | ||
<br> | |||
<h2>The Diamond Problem</h2> | <h2>The Diamond Problem</h2> | ||
<p> | <p> | ||
Line 189: | Line 196: | ||
[[Image:EC_fig7.jpg]] | [[Image:EC_fig7.jpg]] | ||
<br> | <br> | ||
Figure 7: What sound should the CowDuckHybird make? | <i>Figure 7: What sound should the CowDuckHybird make?</i> | ||
<br> | <br> | ||
<br> | <br> | ||
Line 206: | Line 213: | ||
<code> | <code> | ||
class Animal {<br> | class Animal {<br> | ||
String makeSound () {<br> | |||
return "silence"<br> | |||
}<br> | |||
}<br> | }<br> | ||
<br> | <br> | ||
class Cow: public Animal {<br> | class Cow: public Animal {<br> | ||
String makeSound() {<br> | |||
return "moo"; // redefine method<br> | |||
}<br> | |||
}<br> | }<br> | ||
<br> | <br> | ||
class Duck: public Animal {<br> | class Duck: public Animal {<br> | ||
String makeSound() {<br> | |||
return "quack"; // redefine method <br> | |||
}<br> | |||
}<br> | }<br> | ||
<br> | <br> | ||
class CowDuckHybrid: public Cow, public Duck {<br> | class CowDuckHybrid: public Cow, public Duck {<br> | ||
// automatically inherit makeSound() from Cow and Duck<br> | |||
}<br> | }<br> | ||
<br> | <br> | ||
Line 246: | Line 253: | ||
<code> | <code> | ||
class Animal {<br> | class Animal {<br> | ||
String makeSound () {<br> | |||
return "silence"<br> | |||
}<br> | |||
}<br> | }<br> | ||
<br> | <br> | ||
class Cow: public virtual Animal {<br> | class Cow: public virtual Animal {<br> | ||
String makeSound() {<br> | |||
return "moo"; // redefine method<br> | |||
}<br> | |||
}<br> | }<br> | ||
<br> | <br> | ||
class Duck: public virtual Animal {<br> | class Duck: public virtual Animal {<br> | ||
String makeSound() {<br> | |||
return "quack"; // redefine method<br> | |||
}<br> | |||
}<br> | }<br> | ||
<br> | <br> | ||
class CowDuckHybrid: public Cow, public Duck {<br> | class CowDuckHybrid: public Cow, public Duck {<br> | ||
// automatically inherit makeSound() from Cow and Duck<br> | |||
}<br> | }<br> | ||
<br> | <br> | ||
Line 273: | Line 280: | ||
<br> | <br> | ||
</p> | </p> | ||
<br> | |||
<h2>Conceptual Difficulties</h2> | <h2>Conceptual Difficulties</h2> | ||
<p> | <p> | ||
Line 281: | Line 288: | ||
Without good organization of the classes and understanding of how they are organized, multiple inheritance can become more trouble than it is worth. | Without good organization of the classes and understanding of how they are organized, multiple inheritance can become more trouble than it is worth. | ||
</p> | </p> | ||
<br> | |||
<h1>Alternatives to Multiple Inheritance</h1> | <h1>Alternatives to Multiple Inheritance</h1> | ||
<p> | <p> | ||
Line 286: | Line 294: | ||
<br> | <br> | ||
</p> | </p> | ||
<br> | |||
<h2>Alternative in Java</h2> | <h2>Alternative in Java</h2> | ||
<p> | <p> | ||
Line 293: | Line 302: | ||
[[Image:EC_fig8.jpg]] | [[Image:EC_fig8.jpg]] | ||
<br> | <br> | ||
Figure 8: Implementing multiple inheritance in Java | <i>Figure 8: Implementing multiple inheritance in Java</i> | ||
<br> | <br> | ||
<br> | <br> | ||
<code> | <code> | ||
interface Spider {<br> | interface Spider {<br> | ||
void shootWeb();<br> | |||
void climbWalls();<br> | |||
}<br> | }<br> | ||
<br> | <br> | ||
interface Man {<br> | interface Man {<br> | ||
void talk();<br> | |||
void run();<br> | |||
}<br> | }<br> | ||
<br> | <br> | ||
public class Spiderman implements Spider, Man {<br> | public class Spiderman implements Spider, Man {<br> | ||
public void shootWeb() {...}<br> | |||
public void climbWalls() {...}<br> | |||
public void talk() {...}<br> | |||
public void run() {...}<br> | |||
}<br> | }<br> | ||
<br> | <br> | ||
Line 329: | Line 338: | ||
<code> | <code> | ||
public class Spider {<br> | public class Spider {<br> | ||
void shootWeb { ... }<br> | |||
void climbWalls { ... }<br> | |||
}<br> | }<br> | ||
<br> | <br> | ||
public class Man {<br> | public class Man {<br> | ||
void talk() { ... }<br> | |||
void run() { ... }<br> | |||
}<br> | }<br> | ||
<br> | <br> | ||
public class Spiderman {<br> | public class Spiderman {<br> | ||
Spider s;<br> | |||
Man m;<br> | |||
<br> | <br> | ||
// Spiderman specific stuff<br> | |||
}<br> | }<br> | ||
<br> | <br> | ||
public class Venom {<br> | public class Venom {<br> | ||
Spider s;<br> | |||
<br> | <br> | ||
// Venom specific stuff<br> | |||
}<br> | }<br> | ||
</code> | </code> | ||
Line 358: | Line 367: | ||
Both solutions shown are not really multiple inheritance in the true sense, but they can get the job done, depending on the specifications of the design. | Both solutions shown are not really multiple inheritance in the true sense, but they can get the job done, depending on the specifications of the design. | ||
</p> | </p> | ||
<br> | |||
<h2>Modules and Mixins</h2> | <h2>Modules and Mixins</h2> | ||
<p> | <p> | ||
Line 365: | Line 375: | ||
<code> | <code> | ||
module Spider<br> | module Spider<br> | ||
def shootWeb()<br> | |||
// ...<br> | |||
end<br> | |||
def climbWalls()<br> | |||
// ...<br> | |||
end<br> | |||
end<br> | end<br> | ||
<br> | <br> | ||
module Man<br> | module Man<br> | ||
def talk()<br> | |||
// ...<br> | |||
end<br> | |||
def run()<br> | |||
// ...<br> | |||
end<br> | |||
end<br> | end<br> | ||
<br> | <br> | ||
class Spiderman<br> | class Spiderman<br> | ||
include Spider<br> | |||
include Man<br> | |||
// ...<br> | |||
end<br> | end<br> | ||
</code> | </code> | ||
Line 395: | Line 405: | ||
Here are some key rules to modules in Ruby.<sup>8</sup> | Here are some key rules to modules in Ruby.<sup>8</sup> | ||
<br> | <br> | ||
1. It is not a class<br> | |||
2. It cannot be instantiated<br> | |||
3. It can be mixed into a class<br> | |||
<br> | <br> | ||
<br> | <br> | ||
When modules are mixed into a class<sup>8</sup>, | When modules are mixed into a class<sup>8</sup>, | ||
<br> | <br> | ||
1. There is no hierarchy. Even though we included "Spider" and "Man" in class "Spiderman", "Spiderman" object cannot be polymorphized into "Spider" nor "Man". That is, "Spiderman" is not a subclass of "Spider" or "Man" | |||
<br> | <br> | ||
2. Multiple modules can be mixed-in to a class.<br> | |||
</p> | </p> | ||
<br> | <br> |
Latest revision as of 20:33, 21 September 2010
What is inheritance?
Inheritance is a functionality of object-oriented programming where a subclass obtains the contents and functionality of its superclass.
Instead of duplicating a class, a user can create a class that is a subclass of another class, inherit all of its functionality, and just add additional functionality.
For example, if the class "Bike" already exists and we wanted to create specific type of bicycle such as "MountainBike" or "RoadBike", we could just create classes that inherit from the "Bike" class and add the additional functionality we need.
Figure 1: Simple case of inheritance
The use of inheritance is convenient because we did not have to duplicate code to create a new subclass and increases code re-usability1. Also, if changes are made to the superclass, those changes will be reflected in the subclasses as well.
Single Inheritance
For the example above, where a subclass inherits from a single parent class, this is called single inheritance.
Object-oriented languages including Java, support single inheritance. A Java implementation would appear as below.
public class Bike {
// Implementation of generic bike class
// ie. frame, wheels
}
public class MountainBike extends Bike {
// inherit generic bike components
// add shocks, fat tires, etc.
}
public class RoadBike extends Bike {
// inherit generic bike components
// add narrow tires, drop-style handlebars, etc.
}
Multiple Inheritance
To enhance the concept of code re-usability even further, a user may wish to inherit from multiple parent classes. For example, the class "Bike" could have been made up from two classes called "Frame" and "Wheels".
Figure 2: Case of multiple inheritance
The case where a subclass inherits from multiple parent classes is called multiple inheritance. This feature is not supported in all objected oriented languages.
Languages that support multiple inheritance include C++, Python, Perl, Lisp, Tcl2, and Ruby (with the use of mixins).
The C++ implementation of the example is shown below.
class Frame {
// frame of a bike
}
class Wheels {
// wheels of a bike
}
class Bike: public Frame, public Wheels
// create bike by putting together frame and wheels
}
Advantages of Multiple Inheritance
Multiple inheritance allows the programmer to 'mix-and-match' elements from several classes to form a new class. This saves effort from the programmer by not having to duplicate code and modify code when parent classes are changed. For more complicated hierarchies, this feature can quite useful.
Consider the case below where we have "Engine", "Chassis", and "Body" parent classes and two subclasses "Sedan" and "Truck".
The "Sedan" class is a sedan and needs an engine, chassis, and body so it inherits from all three parent classes. Same can be applied for the "Truck" class. The "Sedan" and "Truck" inherits all three components and they can modify them to suit their needs. The sedan could have a longer body to increase passenger room, the truck overrides body for an open cab, and so on.
Assume that we need polymorphic functionality. For example, we need to be able to run the following code:
Sedan s = new Sedan();
Then we pass object 's' to a method somewhere requiring an "Engine" type such as
int testEngine(Engine e);
Since our sedan has an engine we want to do:
testEngine(s);
Let's say later, we want to create a push-type lawnmower. The lawnmower does not need the chassis or body implementations, but needs an engine so we just inherit from the "Engine" class.
Figure 3: Multiple inheritance allows more flexible hierarchies.
With single inheritance, the structure illustrated above would not be possible. Since a subclass cannot inherit from multiple parent classes, the programmer would probably have to combine all three "Engine", "Chassis", and "Body" classes into a single class, perhaps called "Assembly". Then the "Sedan" and "Truck" classes would inherit from this combined class.
Figure 4: Single inheritance limits hierarchy.
The "Lawnmower" class now posses a dilemma. Should we inherit from the "Assembly" class and get a lot of functionality we don't need and possibly introduce unwanted behavior. Or should the code for the engine be duplicated from the "Assembly" class and copied into the "Lawnmower" classes. What happens when engine specifications change, we would need to modify engine code in two places?
Therefore, as the example demonstrates the structure for single inheritance is more constrained.
Disadvantages of Multiple Inheritance
Although multiple inheritance gives programmers more flexibility in class hierarchies and 'mix-and-match' implementation, one would think that it should be included in all object-oriented languages.
The reality is that several implementation issues keep it from being incorporated into some languages.
Some key issues include name and method collision3, the diamond problem4, and conceptual difficulties and understandability problems3.
Name collision
A subclass that inherits from several parents classes with the same variable names will have name collisions.
Figure 5: Which x value will print_x() method print?
It is unclear which value of 'x' would be printed. Different languages and compilers will handle this differently.
Method collision
Similar to name collision, when a subclass inherits from the several parents with the same method names, method collision will occur.
Figure 6: Which show() method will Package inherit?
The Diamond Problem
The diamond problem occurs when the direct base classes are themselves derived from another class5. This can be illustrated in the hierarchy below. The pattern forms a diamond shape, hence the name of the problem.
Figure 7: What sound should the CowDuckHybird make?
Let's define two classes called "Cow" and "Duck". They both inherit from "Animal". The makeSound() method is inherited in both "Cow" and "Duck" classes. The "Cow" class defines the method to return "moo", while the "Duck" class redefines the method to return "quack".
Let's assume that through some strange breeding, a cow and duck breed to create a "CowDuckHybird" classes which inherits from both "Cow" and "Duck" parents. When the makeSound() method is called on a "CowDuckHybrid" object, what sound will make?
In a multiple inheritance supporting language such as C++, the "CowDuckHybrid" class would implement both makeSound() methods, and the programmer has to explicitly specify which one to use.
The code implementation in C++ illustrates this kind of problem below.
class Animal {
String makeSound () {
return "silence"
}
}
class Cow: public Animal {
String makeSound() {
return "moo"; // redefine method
}
}
class Duck: public Animal {
String makeSound() {
return "quack"; // redefine method
}
}
class CowDuckHybrid: public Cow, public Duck {
// automatically inherit makeSound() from Cow and Duck
}
// Main code
Animal a;
a.makeSound(); // illegal statement - compiler will complain
a.Cow::makeSound(); // outputs "moo"
a.Duck::makeSound() // outputs "quack"
In C++, the programmer must explicitly specify which duplicate method to run. If not, the compiler will throw an error.
There is also additional functionally in C++ to address the issue of diamond-like hierarchies. The concept of virtual base classes can make a class appear to be virtual or transparent. By making the two intermediate classes "Duck" and "Cow" virtual, we tell the compiler not to make duplicate variables or methods in "CowDuckHybrid" or any classes derived from the two virtual classes5.
The implementation with virtual classes is shown below.
class Animal {
String makeSound () {
return "silence"
}
}
class Cow: public virtual Animal {
String makeSound() {
return "moo"; // redefine method
}
}
class Duck: public virtual Animal {
String makeSound() {
return "quack"; // redefine method
}
}
class CowDuckHybrid: public Cow, public Duck {
// automatically inherit makeSound() from Cow and Duck
}
// Main code
Animal a;
a.makeSound(); // now legal, outputs "silence"
Conceptual Difficulties
For large projects, where there are several layers of classes and inheritance from many classes, the amount of conflicts can become high and resolving those conflicts can become very tedious. In languages such as C++, where multiple inheritance is supported, the programmer must explicitly indicate which parent class method or variable to use, or resolve the conflict by using the virtual keyword functionality. This, however, requires the programmer to trace back to the parent classes to determine what course of action to take.
Without good organization of the classes and understanding of how they are organized, multiple inheritance can become more trouble than it is worth.
Alternatives to Multiple Inheritance
Many object-oriented languages do not use multiple inheritance for the reasons mentioned above. One of those languages is Java. Part of the philosophy of the Java language is to be simple and ease of use, and the creators of Java decided not to have support for multiple inheritance.
Alternative in Java
Java can simulate multiple inheritance through the use of multiple interfaces6,7. For example, the hierarchy below can be implemented in Java.
Figure 8: Implementing multiple inheritance in Java
interface Spider {
void shootWeb();
void climbWalls();
}
interface Man {
void talk();
void run();
}
public class Spiderman implements Spider, Man {
public void shootWeb() {...}
public void climbWalls() {...}
public void talk() {...}
public void run() {...}
}
// Main code
Spider s = new Spiderman(); // all of these statements are valid
Man m = new Spiderman();
Spiderman sm = new Spiderman();
Note that in this implementation, we would have to implement the "Spider" and "Man" methods in the "Spiderman" class. We cannot include code in the interface, as this violates the concept of interfaces in Java. We loose code re-usability, but persevered polymorphic relationship. If we created another class called "Venom" and implemented "Spider" class, we would have to duplicate the code.
On the other hand, if we don't care about polymorphism and just don't want to duplicate code, we can just compost the code like the following.
public class Spider {
void shootWeb { ... }
void climbWalls { ... }
}
public class Man {
void talk() { ... }
void run() { ... }
}
public class Spiderman {
Spider s;
Man m;
// Spiderman specific stuff
}
public class Venom {
Spider s;
// Venom specific stuff
}
Here both "Spiderman" and "Venom" can reuse the "Spider" class code without having to implement it twice. However, "Spiderman" cannot be considered a "Spider" nor a "Man".
Both solutions shown are not really multiple inheritance in the true sense, but they can get the job done, depending on the specifications of the design.
Modules and Mixins
Ruby can simulate multiple inheritance using modules. In Ruby the code appears as follows.
module Spider
def shootWeb()
// ...
end
def climbWalls()
// ...
end
end
module Man
def talk()
// ...
end
def run()
// ...
end
end
class Spiderman
include Spider
include Man
// ...
end
In Ruby, the module code is simply "mixed in" to the specified class, basically a copy and paste function.
Here are some key rules to modules in Ruby.8
1. It is not a class
2. It cannot be instantiated
3. It can be mixed into a class
When modules are mixed into a class8,
1. There is no hierarchy. Even though we included "Spider" and "Man" in class "Spiderman", "Spiderman" object cannot be polymorphized into "Spider" nor "Man". That is, "Spiderman" is not a subclass of "Spider" or "Man"
2. Multiple modules can be mixed-in to a class.
References
1. Object-Oriented Design Using Java. Dale Skrien. 2009. pp. 10-30
2. Wikipedia - Multiple Inheritance
3. CSC517 Lec. 9 Notes. Dr. Gehringer. 2010
4. Wikipedia - Diamond Problem
5. Beginning Visual C++. Ivor Horton. 1998. pp. 422-424.
6. JavaWorld - Designing with interfaces
7. Implement multiple interfaces
8. An Introduction to modules