CSC/ECE 517 Fall 2007/wiki1 7 a23: Difference between revisions
No edit summary |
No edit summary |
||
(20 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
This Wiki page was created by Chris Laurey. All explanations and examples have been created by the author. Code examples are included in the text because they are relatively short. | |||
This Wiki page | |||
'''This Wiki page provides a discussion of the following questions on multiple inheritance.''' | |||
Line 13: | Line 14: | ||
'''What is wrong with multiple inheritance?''' | |||
== '''What is wrong with multiple inheritance?''' == | |||
Before answering Questions 1, 2, and 3, it is useful to understand what multiple inheritance is and what can be problematic about it. | Before answering Questions 1, 2, and 3, it is useful to understand what multiple inheritance is and what can be problematic about it. | ||
Multiple inheritance is a feature of some programming languages that allows a class to inherit from more than one class. This means that if class A inherits from class B and class C, then A will have the methods and class members defined in classes B and C. | Multiple inheritance is a feature of some programming languages that allows a class to inherit from more than one class. This means that if class A inherits from class B and class C, then A will have the methods and class members defined in classes B and C. | ||
One programming language that supports multiple inheritance is C++. Consider the following code that defines the class Apple. Notice that it inherits from classes Fruit and Farmer. | One programming language that supports multiple inheritance is C++. Consider the following code that defines the class Apple. Notice that it inherits from classes Fruit and Farmer. | ||
Line 33: | Line 40: | ||
At first glance, multiple inheritance appears to be a nice feature that allows for substantial code reuse. If we consider what could be in both classes, however, problems can quickly arise. What if both class Fruit and class Farmer have the same method getLocation()? How will the compiler know which method to call? Consider the following partial class definitions for Fruit and Farmer: | At first glance, multiple inheritance appears to be a nice feature that allows for substantial code reuse. If we consider what could be in both classes, however, problems can quickly arise. What if both class Fruit and class Farmer have the same method getLocation()? How will the compiler know which method to call? Consider the following partial class definitions for Fruit and Farmer: | ||
class Fruit{ | class Fruit{ | ||
Line 53: | Line 61: | ||
string getProducerName(); | string getProducerName(); | ||
}; | }; | ||
For a static language such as C++, the compiler will be unable to resolve the ambiguity and pick the correct method. As a result, if a developer wishes to use multiple inheritance, he or she will have to make sure that all classes that could be inherited along with another class don't have conflicting member or method names. | For a static language such as C++, the compiler will be unable to resolve the ambiguity and pick the correct method. As a result, if a developer wishes to use multiple inheritance, he or she will have to make sure that all classes that could be inherited along with another class don't have conflicting member or method names. | ||
The problem is even worse that that! The developer will also have to check any modifications to a class that could be inherited against all the other classes that could be inherited. | The problem is even worse that that! The developer will also have to check any modifications to a class that could be inherited against all the other classes that could be inherited. | ||
As a result, multiple inheritance can require substantial development time, ongoing maintenance, and can easily lead to messy class hierarchies, and error-prone code. | As a result, multiple inheritance can require substantial development time, ongoing maintenance, and can easily lead to messy class hierarchies, and error-prone code. | ||
== '''Question 1''': ''"Do mixins solve all of these problems?'' - '''Answer''' == | |||
Mixins allow a class to use the methods defined in a module by including the name of that module in the class definition. Consider the following code: | |||
module Benefits | |||
# other code... | |||
def Benefits.outputCompensation() | |||
# other code... | |||
end | |||
end | |||
class Worker | |||
include Benefits | |||
# other code... | |||
end | |||
This technique allows the developer to efficiently reuse code by "mixing-in" code into a class that is defined elsewhere, and dynamically change code in a module while a program is running. Despite these advantages, mixins do not solve all of the problems of multiple inheritance. | |||
The developer must still be careful to avoid situations where a method defined in a class (or in another included module) has the same name as one defined in an included module. Furthermore, the developer should still plan how much functionality should be in a given module, and decide what method names are appropriate. | The developer must still be careful to avoid situations where a method defined in a class (or in another included module) has the same name as one defined in an included module. Furthermore, the developer should still plan how much functionality should be in a given module, and decide what method names are appropriate. | ||
While the problems of multiple inheritance remain with the use of mixins, their impact is somewhat less serious than with a static language such as C++. This is because dynamic languages, such as Ruby, excel in prototyping, where the focus is on constructing a model and not on a robust application. | While the problems of multiple inheritance remain with the use of mixins, their impact is somewhat less serious than with a static language such as C++. This is because dynamic languages, such as Ruby, excel in prototyping, where the focus is on constructing a model and not on a robust application. | ||
== '''Question 2''': ''"Are mixins a clear advance over interfaces?"'' - '''Answer''' == | |||
Some languages approach the problems of multiple inheritance by allowing only single inheritance, and offering features that provide some of the benefits of multiple inheritance. | |||
Java prohibits multiple inheritance. Each class can only inherit from a single parent class, and each parent can have multiple children. This substantially mitigates coding issues posed by multiple inheritance, but it limits the degree of code reuse. | Java prohibits multiple inheritance. Each class can only inherit from a single parent class, and each parent can have multiple children. This substantially mitigates coding issues posed by multiple inheritance, but it limits the degree of code reuse. | ||
Java provides a partial solution with interfaces, which allow a class to use previously defined function declarations by using the keyword implements in the class signature and by defining all of the methods declared in the interface in the class that is implementing them. Consider the following code: | |||
Java provides a partial solution with interfaces, which allow a class to use previously defined function declarations by using the keyword "implements" in the class signature and by defining all of the methods declared in the interface in the class that is implementing them. Consider the following code: | |||
Line 101: | Line 136: | ||
The benefit of interfaces is that they can create a standard function call, so that different objects may call a method the same way, even if the method has different implementation. This can be useful for the developer because they do not have to keep track of different function calls for the same type of method | The benefit of interfaces is that they can create a standard function call, so that different objects may call a method the same way, even if the method has different implementation. This can be useful for the developer because they do not have to keep track of different function calls for the same type of method from one object to the next. Interfaces can also be useful to control development with a team of developers. | ||
While interfaces safeguard against ambiguity issues posed by multiple inheritance, they do have some disadvantages. A class must define all of the methods in an interface it implements in order to instantiate objects. Otherwise, it will be an abstract class. As a result, interfaces require careful planning and significant maintenance. | While interfaces safeguard against ambiguity issues posed by multiple inheritance, they do have some disadvantages. A class must define all of the methods in an interface it implements in order to instantiate objects. Otherwise, it will be an abstract class. As a result, interfaces require careful planning and significant maintenance. | ||
Mixins provide some advantages over interfaces. When a module is included, it need not be defined in the class that is including it. This reduces the amount of code that a developer has to write, and allows the developer to focus on the immediate coding task. However, it is incorrect to say that mixins are a clear advance over interfaces. This is because mixins still possess ambiguity issues posed by multiple inheritance when a class includes multiple modules with the same method names. | Mixins provide some advantages over interfaces. When a module is included, it need not be defined in the class that is including it. This reduces the amount of code that a developer has to write, and allows the developer to focus on the immediate coding task. However, it is incorrect to say that mixins are a clear advance over interfaces. This is because mixins still possess ambiguity issues posed by multiple inheritance when a class includes multiple modules with the same method names. | ||
== '''Question 3''': ''"Do mixins have any disadvantages not shared by multiple inheritance or interfaces?"'' - '''Answer''' == | |||
To use mixins with just an include command, the module must be in the same file as the class definition. If the module is not in the same class definition, a require or load command must also be used before the include command. This requires the developer to code differently depending on the location of the module to be included. With C++ and multiple inheritance, and Java and interfaces, the developer is used to including necessary files during the development process, and will be given compilation errors if code in a file is not referenced properly. | |||
'''Conclusion''' | A disadvantage of mixins, and of dynamic languages generally, is that the developer is less likely to receive correct notification of a problem if there is a conflict with mixing in multiple modules. The risk is that a problem may be misdiagnosed, or not diagnosed at all, and is more likely to persist than in static languages that support multiple inheritance or interfaces. | ||
== '''Conclusion''' == | |||
Line 125: | Line 165: | ||
''For more information on multiple inheritance, interfaces, and mixins, see'' | |||
== ''For more information on multiple inheritance, interfaces, and mixins, see'' == | |||
"Absolute C++", by Walter Savitch | "Absolute C++", by Walter Savitch | ||
http://www.mycplus.com/cplus.asp?CID=62 | http://www.mycplus.com/cplus.asp?CID=62 | ||
"Java in a Nutshell", by David Flanagan | "Java in a Nutshell", by David Flanagan | ||
"Programming Ruby", by Dave Thomas | "Programming Ruby", by Dave Thomas |
Latest revision as of 01:04, 20 September 2007
This Wiki page was created by Chris Laurey. All explanations and examples have been created by the author. Code examples are included in the text because they are relatively short.
This Wiki page provides a discussion of the following questions on multiple inheritance.
"Multiple inheritance is a controversial concept. Detractors say it leads to messy class hierarchies, it is impossible to implement efficiently, and when the same method is inherited along two different paths, there is no good way to decide automatically which definition should be used."
(1) "Do mixins solve all of these problems? Give (or cite) examples (e.g., Java, C++, and/or Ruby) to illustrate your conclusions."
(2) "Are mixins a clear advance over interfaces?"
(3) "Do mixins have any disadvantages not shared by multiple inheritance or interfaces?"
What is wrong with multiple inheritance?
Before answering Questions 1, 2, and 3, it is useful to understand what multiple inheritance is and what can be problematic about it.
Multiple inheritance is a feature of some programming languages that allows a class to inherit from more than one class. This means that if class A inherits from class B and class C, then A will have the methods and class members defined in classes B and C.
One programming language that supports multiple inheritance is C++. Consider the following code that defines the class Apple. Notice that it inherits from classes Fruit and Farmer.
class Apple: public Fruit, public Farmer{
protected: string color; public: Apple(); string getColor();
};
At first glance, multiple inheritance appears to be a nice feature that allows for substantial code reuse. If we consider what could be in both classes, however, problems can quickly arise. What if both class Fruit and class Farmer have the same method getLocation()? How will the compiler know which method to call? Consider the following partial class definitions for Fruit and Farmer:
class Fruit{
protected: string location; int weight; public: Fruit(); string getLocation(); int getWeight();
};
class Farmer{
protected: string location; string producerName; public: Farmer(); string getLocation(); string getProducerName();
};
For a static language such as C++, the compiler will be unable to resolve the ambiguity and pick the correct method. As a result, if a developer wishes to use multiple inheritance, he or she will have to make sure that all classes that could be inherited along with another class don't have conflicting member or method names.
The problem is even worse that that! The developer will also have to check any modifications to a class that could be inherited against all the other classes that could be inherited.
As a result, multiple inheritance can require substantial development time, ongoing maintenance, and can easily lead to messy class hierarchies, and error-prone code.
Question 1: "Do mixins solve all of these problems? - Answer
Mixins allow a class to use the methods defined in a module by including the name of that module in the class definition. Consider the following code:
module Benefits
# other code... def Benefits.outputCompensation() # other code... end
end
class Worker
include Benefits # other code...
end
This technique allows the developer to efficiently reuse code by "mixing-in" code into a class that is defined elsewhere, and dynamically change code in a module while a program is running. Despite these advantages, mixins do not solve all of the problems of multiple inheritance.
The developer must still be careful to avoid situations where a method defined in a class (or in another included module) has the same name as one defined in an included module. Furthermore, the developer should still plan how much functionality should be in a given module, and decide what method names are appropriate.
While the problems of multiple inheritance remain with the use of mixins, their impact is somewhat less serious than with a static language such as C++. This is because dynamic languages, such as Ruby, excel in prototyping, where the focus is on constructing a model and not on a robust application.
Question 2: "Are mixins a clear advance over interfaces?" - Answer
Some languages approach the problems of multiple inheritance by allowing only single inheritance, and offering features that provide some of the benefits of multiple inheritance.
Java prohibits multiple inheritance. Each class can only inherit from a single parent class, and each parent can have multiple children. This substantially mitigates coding issues posed by multiple inheritance, but it limits the degree of code reuse.
Java provides a partial solution with interfaces, which allow a class to use previously defined function declarations by using the keyword "implements" in the class signature and by defining all of the methods declared in the interface in the class that is implementing them. Consider the following code:
public interface Compensation {
double calculateIncome(double wage, double hours); double calculateBenefits(int daysWorked);
}
public class Developer extends Worker implements Compensation, HobbyInterests {
// Member declarations and methods of class Worker public double calculateIncome(double wage, double hours){ // Code to implement calculateIncome method } public double calculateBenefits(int daysWorked){ // Code to implement calculateBenefits method }
// Method definitions of HobbyInterests interface
}
The benefit of interfaces is that they can create a standard function call, so that different objects may call a method the same way, even if the method has different implementation. This can be useful for the developer because they do not have to keep track of different function calls for the same type of method from one object to the next. Interfaces can also be useful to control development with a team of developers.
While interfaces safeguard against ambiguity issues posed by multiple inheritance, they do have some disadvantages. A class must define all of the methods in an interface it implements in order to instantiate objects. Otherwise, it will be an abstract class. As a result, interfaces require careful planning and significant maintenance.
Mixins provide some advantages over interfaces. When a module is included, it need not be defined in the class that is including it. This reduces the amount of code that a developer has to write, and allows the developer to focus on the immediate coding task. However, it is incorrect to say that mixins are a clear advance over interfaces. This is because mixins still possess ambiguity issues posed by multiple inheritance when a class includes multiple modules with the same method names.
To use mixins with just an include command, the module must be in the same file as the class definition. If the module is not in the same class definition, a require or load command must also be used before the include command. This requires the developer to code differently depending on the location of the module to be included. With C++ and multiple inheritance, and Java and interfaces, the developer is used to including necessary files during the development process, and will be given compilation errors if code in a file is not referenced properly.
A disadvantage of mixins, and of dynamic languages generally, is that the developer is less likely to receive correct notification of a problem if there is a conflict with mixing in multiple modules. The risk is that a problem may be misdiagnosed, or not diagnosed at all, and is more likely to persist than in static languages that support multiple inheritance or interfaces.
Conclusion
Multiple inheritance, interfaces, and mixins all have strengths and weaknesses. No one approach is superior. Rather, depending on a given programming or software engineering task, one approach may fit best. Multiple inheritance allows for substantial code reuse, provided there is excellent planning and communication between developers about the structure of the functions of a program. Interfaces are most appropriate where a programming project needs to be robust, but the scope of the project and lack of near perfect communication between developers requires greater safeguards in the architecture of a program. Mixins are perhaps most appropriate for a programming project whose goal is to efficiently provide a prototype that can be used to model an idea or provide feedback.
For more information on multiple inheritance, interfaces, and mixins, see
"Absolute C++", by Walter Savitch
http://www.mycplus.com/cplus.asp?CID=62
"Java in a Nutshell", by David Flanagan
"Programming Ruby", by Dave Thomas