Multiple Inheritance allows the creation of a class definition using two or more superclasses as the ancestors of the new class.
I'm not sure about flawed design. But I am using Rick Strahl's "Webstore" application. He has a default cBusiness class and all other classes (like cCustomer, cInvoice) are subclasses of this.
DEFINE CLASS cBusiness AS custom
DEFINE CLASS cInvoice AS cBusiness
I have created a htxBusiness class which is my variant of cBusiness, and htxCustomer, htxInvoice which are my variants of cCustomer and cInvoice.
htxInvoice is a subclass of cInvoice, but this means that any variant coding I put in htxBusiness doesn't get picked up in htxInvoice.
DEFINE CLASS htxBusiness AS cBusiness
DEFINE CLASS htxInvoice AS ??????
If I say "AS cInvoice" then the next class up becomes "cBusiness".
If I say "AS htxBusiness" then the next class up is "cBusiness", but the coding in "cInvoice" is just not available.
What I really want is
DEFINE CLASS htxInvoice AS htxBusiness AND cInvoice -- Peter Somers
One way to do this is with Class Insulation Layers
If you find you need it you probably have a flawed design. I can't think of a single case where it would be needed in a language like Visual FoxPro where there is no type checking. It can be useful in some cases in a language like C++ which does type checking. -- Maurice De Beijer
When you do complex modeling of business structures and processes multiple inheritance is often required. -- ?jMM
I would not say it is ever required, but it can be helpful. Anything that can be done with multiple inheritance can also be done without it. -- Jim B
Java does not support multiple inheritance of class hierarchy. Java does support implementation of multiple interfaces. This is a very useful feature of the language. It allows Java classes to present a public interface that "looks like" multiple inheritance without the design problems of multiple inheritance. Java interfaces are defined as pure abstract methods. A Java class that implements an interface must provide code in each method of the interface. Classes in different single inheritance trees can choose to implement some common set of interfaces. I think it would be nice VFP supported this type of interface inheritance. I have a set of container classes that all derive from a single cObject class. cObject includes a cErrorStatus property and a couple of associated methods. Several subclasses of cObject will be used on a Form, the Form needed to support this "Error Interface" which meant that work had to be duplicated to define the interface in the Form class tree and provide implementation. -- David Frankenbach
See Inheritance In Java
I can think of a few places where I'd use multiple inheritance. Since unlike some other languages, VFP doesn't a common ancestor object (eg. a class called Object that everything, even Text Box and Spinner, is subclassed from), common behavior you want added to all (or most) classes involves copying and pasting code. One example of this is Error Handling, in which every base class has the same behavior (delegate to a Chain Of Responsibility error handling scheme). Another (for visual classes, anyway) is shortcut menu handling, in which the basics of a shortcut menu are implemented and the details left to the subclasses. -- Doug Hennig
IMHO I don't think either of these examples requires or even desires multiple inheritance. The basic test for inheritance versus composition is "is a" versus "contains a" and these examples are neither an error handler nor a popup menu they just contain one. So composition would be the better solution. As both contain a single method as interface this may not even require an additional class as a single procedure would do the trick.
Additionally multiple inheritance wouldn't solve the problem as the Error method exists in both classes. In C++ this would be an error and require an Error method in the subclass to resolve which of the two needs to be called.
Another point is that the Error method is the interface just like Click and it is a good design principle to separate the interface for the implementation. So implementing the error handling in the Error method seems like a bad design for more than one reason. -- Maurice De Beijer
The notions of is-a and has-a (type-of vs composition) are one of those eye-of-the-beholder things. As a human being I have an error handler system. I call it an immune system. I can say, "I have an immune system," but is my immune system in a is-a or has-a relationship with my body? The real answer to the question seems to be found in asking the negative question: Is it common to have a human body without an immune system? The real answer is: No, the great majority of human bodies have an immune system. An immune system makes little sense without a body to protect and be a part of.
As for the software error handler, the idea is the same. Does an error handler make much contextual sense outside of a host class? The answer is: No, for the most part, an error handling system only makes sense in the context of other software that might produce errors. This is the same as saying, "An immune system only makes sense in the context of a body that needs protection from disease." So, the two notions are TIGHTLY coupled.
We can certainly create an error handler class, but that class only makes sense when coupled to a software class of some sort. Hence, we might create our error handling class with common behavior, but we want MI so our classes can inherit from it and then specialize the behavior of the error handling technology for each particular class.
What are our other choices?
1. We can create a single error handler class that tries to manage and know what to do in every class context. This is a VERY daunting task.
2. We can duplicate the same structure and code of an error handler on all our classes. Again, this is a lot of work.
3. We can use MI to create it once, inherit from it and then manage the specializations as they arise.
Perhaps there are more choices, perhaps not. What is clear is: MI gives us a more sensible road towards reuse and modularization. There are two more things that I want to say before leaving off the topic of Multiple-Inheritance.
First, MI as it is implemented in C++ should NOT be used as the best example of what it means to have or use MI in a living programming system. It is far from it. Try to discern the line between the technology as an idea or notion and a specfic implementation (i.e. C++).
The same thing can be said for Java interfaces. This is just another spin on the same matter. It seems like someone had a "beef" with the C++ implementation of MI and said, "I'll not make that mistake," but never went so far as to ask, "Is there a good theory in OO that handles and manages MI well that I can use as the basis for building Java interfaces?" The implementation of Java interfaces seems more of a knee-jerk reaction to a bad implementation in C++.
Second, there IS a language that comes quite a bit closer to a good implementation of MI and that is Eiffel. Perhaps there are others as well. This is not a plug for Eiffel and a bashing session for VFP, C++ and Java. All I really want to point towards is there is a good and useful technology called Multiple Inheritance. It does have sensible and reasonable rules and can be implemented in a way that is useful.
Can MI be implemented in VFP in a simple, useful and straightforward manner? To answer this question, what we need to point at and see first is: What are we talking about? Design-time or Run-time? Asked another way: Do we want to implement our MI when we're writing our code or running it?
I see the choices for MI at design-time as:
1. Copy and Paste - This is the common thing to do. The error handler is a good example.
2. 3rd Party MI Tool - I have seen such a tool, but I can envision what it looks like. It lives as a plug-in hook tool that creates VCX or PRG files with duplicated code, but through the tool there is only one point of code.
Personally, I like the run-time solution better (I have done both). Copy and paste is a terrible waste of time and prone to errors. One small change can affect a huge amount of code. I don't have a 3rd Party MI Tool, so what choices remain?
The best choice I see in VFP for implementing MI is to create Java-esque interfaces, bolt on a run-time instance of what I want to inherit and then have the interface methods call and use the methods and properties of the same names on the object added at run-time. While there is still some overhead in maintaining multiple method and properties names on multiple classes, the really important parts of code are stored in one place.
An interesting thing to note is, I can take some of my labor at design-time (i.e. method creation) and park it in a constructor class (pattern) at run-time. What I really need to do is just pay attention to keeping my architecture working consistent throughout the framework of my code. Also, I have to have a way to recognize in the design-time side when I have implemented MI. This is a burden, but there are ways to handle that as well.
Admittedly, there is a thing that MI will NOT solve: verbosity. Say I have a child class that needs 90% of the code in my parent, MI class. Pretend there are 100 lines of code and 10 of them don't work for the child, but need to be adapted (specialized). I seem to have two basic choices (this is true even in a GOOD implementation of MI):
1. I can copy out all the code of the parent, paste it in the child and then edit the 10 lines.
2. I can refactor the parent method to three or more methods and place the appropriate calls in the child: overriding (NODEFAULT) the code of the parent.
Solution 1 is quick and expedient in the spur-of-the-moment. Later it comes to haunt me though, when I find that I have to make a change in the other 90 lines shared by both parent and child. If I make the change in the one, but not the other, or if I make the correct change in the one, but goof it up in the other, then I am stuck. It's now time to debug.
The second solution requires more front-end work, but the back end is simpler. My code is now in one place and called twice -- once in the parent and once in the child. So, if I have the same code change to make as in the example from the previous paragraph, I have one place to go for making my change and one place to look if it fails.
The "art" part of computer programming is the hedging of bets that we do in deciding how much refactoring we'll do as we go. It seems expensive in the front-end, but the back-end usually pays off. MI problems are no exception to this rule. But -- I am digressing.
In the end, MI can be done in VFP and I think there is ample evidence to say a run-time implementation outwieghs the drawbacks of a design-time copy-and-paste method.
Oh yes, one more thing. If you happen to think, I can do without MI in VFP (or any language for that matter) ask yourself this question: Do I copy-and-paste great amounts of code?, or Did I inherit a system with lots of copy-and-pasted duplicated code? If your answer to either or both is yes, you're using one very labor intensive implementation of MI already! The net result is you get to work twice as hard (or perhaps 3x or 4x or more) as you could if you were using either MI or pattern-based implementations of MI in your systems.
Without a true design-time MI, the best we can do is use surrogate run-time design patterns, which is good enough. VFP does give us the tools we need to make this happen in a well organized and well-designed system.
There is a VFP framework from IAS that is available & simulates multiple inheritance. See http://www.iasadvance.com/Products/IAS_SmartTools.htm A former employer bought a few licenses to this product but we never put anything to use while I was there.
The link reference to this framework from "IAS" appears to be obsolete. Pablo Rivera
It's basically two sets of tools in one, Object Talk and Smart Tools. In the Object Talk suite, there's a thing called IAS Prometheus that allows you to retro fit the Object Talk classlibs onto your existing classlibs in the same manner multiple inheritance would allow you to do. FWIW it seemed like a bunch of useless smoke and mirrors to me when I reviewed the product in 1999. -- Rox
I'm finding this topic a bit thick to wade through. So let me give an example from high school geometry.
Suppose I create a class geo. The class represents closed geometric figures and has properties area and periphery (circumference or edge length). I create subclasses circle (with area pi*r^2 and circumference 2*pi*r) and square (area a^2, edge length 4a).
Now I create a class of solids of uniform thickness. The base class, solid, has properties material, density, thickness, volume and weight. The first three are obvious, volume is area*thickness and weight is volume*density. So it needs to be a subclass of geo, because it needs to get at the area.
Now I want to create a thick circle and a thick square (suppose I'm going to stamp these shapes out of sheet metal of uniform thickness).
I want to create a cylinder class and I want to say something like
DEFINE CLASS cylinder AS circle AND solid
That is, I want to define the properties of a circle only once (area and circumference) and the properties that come from thickening such a geometric shape (volume, weight) only once.
I'd be hard pressed to say this was a trivial problem or one that came from poor formulation of the problem, one that re-formulation with the "right" model would solve. This is a real world, concrete problem. I can't "look at my business model differently". This is the real world problem to be solved. I don't see any elegant solution other than multiple inheritance.
I guess I could trap object creation and insert the properties and methods I want from the "other class" at that time, but quite frankly, I'd rather the language did it :-) Michael Wagner
Metaphors and simple examples are a poor way to explore a problem like this. The class geo needs a "dimension" property that describes how many dimensions the geometric figure has. When dimension = 2, circles and squares work, when 3, spheres and cylinders. Volume is undefined for dimension = 2. Surface area is calculated differently for 2 and 3 dimensional objects. 4 dimensional objects have additional attributes that might be undefined for lesser objects.
Inheritance should not be about properties but about methods. It is behaviors that should define classes, not properties. Supporting multiple inheritance for the purposes of presenting an interface to interact with calling objects can be done with IMPLEMENTS. However, if you need to merge behaviors of two parent classes, you need to reexamine your assumptions. jomo -- tr
I don't understand how this explanation helps me.
For a start, you are describing a different geometry. I haven't yet succeeded in stamping a 4 dimensional object out of metal strip.
The geometry I'm describing is the set of 2-d closed shapes and the resulting (relatively) thin 3D slugs you will take out of a metal strip when you punch it with a punch of that shape (against a die of the suitable slighly larger shape).
I didn't describe a surface area the way you did. My "area" is the area of the 2D shape. It remains the same in 3D, because it's the shape of the slug being punched out (or the hole remaining, depending on which was the piece of value and which was the scrap). However, the edge length times the thickness times the yield strength of the material is the cutting force needed. And so forth. There are many calculations. I didn't go into them all, because my simple example already illustrated the problem.
This problem is all about the methods. I can create properties like area that are defined or undefined dynamically (at object creation time and when a property like your "dimension" changes). The problem is that I cannot create dynamically at run time (or even copy from a repository somewhere) the method code. As I said in my posting, all solid objects have certain properties (and the supporting methods for calculating them) like material, density, volume and so on.
Some times I want to model circles as the 3D thin slugs they stamp out, with those solid properties. So I have the geo class. Sometimes I want to model such things by drawing them. So I have the drawing class. I don't want to code circle twice, once subclassed from geo, once from draw. And it's not because I'm too lazy to duplicate a few lines of code for circle. There are literally hundreds of these punch end shapes I can buy off the shelf - maintaining 2 or 3 sets of each of even a hundred shaped punches is a monumental task.
I guess I could build a super subclass with everything I might ever want to do built in to it, with all the methods but the ones I want at the moment turned off with switches in their head code, but that seems like an enormous amount of work.
And I don't understand your comment about IMPLEMENTS. I can't find anything like that in Foxpro.
Contributors: Maurice De Beijer David Frankenbach Doug Hennig jMM Peter Somers Jim B
Category Class Design Category Exam 70-155 Hot Topic
( Topic last updated: 2006.03.26 11:44:50 PM )