Re: •Re: inheritance and object creation
by knew (Monk) on Feb 23, 2004 at 22:49 UTC
|
Ah, yes. I knew i was missing something there, but couldn't kick my brain into gear enough to see it. Ok, so sorting out the inheritance (and adding in a class i forgot to include) gives us (?):
/-----------\ /-------------------\
| Company | | Company::Division |
\-----------/ \-------------------/
/|\ /|\
| | (is-a)
| |
| /-------------------\
| (is-a) | AcmeLtd::Division |
| \-------------------/
| /|\
| | (is-a)
| |
/-----------\ /-------------\
| AcmeLtd |------>------| AcmeLtd::IT |
\-----------/ (has-a) \-------------/
With that we straight away dump #3. I'm still undecided between #1 and #2 though; what are you thoughts on the matter? | [reply] [d/l] |
|
|
something else to keep in mind, is the distinction between "IS-A" and "is-instance-of"
When designing a class hierarchy, the concepts of IS-A and HAS-A get thrown arround alot, but you have to always keep in mind that you are talking about "classes of things" -- not hte things themselves.
All i know about your data and your application is what you've posted in this thread, so maybe there's more going on then i understand, but it seems to me that "AcmeLtd" is an instance-of "Company", and "AcmeLtd IT" is an instance-of "Company::Division". Unless of course, you have some reason to instantiate many different instances of AcmeLtd ?
The nature of your data confuses me, so instead let's Imaging you are dealing with Animals. you've got the class "Animal" and hte subclasses Dog and Cat (a Dog IS-A Animal). now based on the nature of your data, do you want lots of instances of "Dog" representig a breed as an attribute, or do you want a seperate subclass for each breed, with each instance representing a physical dog with a name?
This...
# Dog ISA Animal
my $german_sheperd = new Dog(BIG, FURRY);
my $collie = new Dog(MEDIUM, SHORT_HAIRED);
my $poodle = new Dog(SMALL, GOOFY);
...verses, this...
# Dog ISA Animal
# Dog::Poodle ISA Dog
# Dog::Collie ISA Dog
my $fluffy = new Dog::Poodle('fluffy');
my $spot = new Dog::Poodle('spot');
my $fido = new Dog::Collie('fido');
my $stray = new Dog();
| [reply] [d/l] [select] |
|
|
The latter, a seperate subclass for each breed. I'll try and explain why, and maybe it'll help make things a bit clearer (fingers crossed).
The Company and Company::Division classes are a kind of framework, which AcmeLtd and AcmeLtd::IT build upon to provide specific functionality, overriding methods to do so.
..........
..........
you know, at this point lots of stuff has just clicked into place in my head, and i've realised that by changing some other stuff around, I can get rid of the requirement to start with the Company object, and just start directly with AcmeLtd (which will of course inherit generic processing functionality from Company).
I think the problems i've had in trying to figure out how to structure this have come about because i don't have the AcmeLtd class in the system right now, and all the information that is going to go into that is pulled out of config files. When i decided to add the class in, i didn't analyse the change fully, and have been trying to do things the hard way as a result.
Thanks enormously for the above reply; in trying to answer it's made me think more about the situation, and helped me understand things better myself.
hossman++ # would have voted the above reply up if i could have
| [reply] |
|
|
/-----------\ /-------------------\
| Company |--@(has-N)-> | Company::Division |
\-----------/ \-------------------/
/|\ /|\
| | (is-a)
| |
| /- - - - - - - - - -\
| (is-a) AcmeLtd::Division ?
| \- - - - - - - - - -/
| /|\
| | (is-a)
| |
/-----------\ /-------------\
| AcmeLtd |-----create--> | AcmeLtd::IT |
\-----------/ \-------------/
A company may have more than one division, and the number may change over time, but the distinction between them doesn't really matter. So consider a collection of generic divisions.
In the AcmeLtd construction, or later in its operation, you may choose to add an AcmeLtd::IT instance to the collection of generic divisions of the company. You may choose to nix it later, or outsource it to some Vendor::IT that has a similar interface.
If there's nothing particularly distinguishing about all AcmeLtd::Divisions that would not be required of any generic Company::Division, then kill that class.
(ASCII is not a very good way to draw Booch diagrams.)
-- [ e d @ h a l l e y . c c ]
| [reply] [d/l] |
Re: inheritance and object creation
by Abigail-II (Bishop) on Feb 23, 2004 at 23:27 UTC
|
If the answer to a question is "multiple inheritance", you've probably asked the wrong question.
Really? Suppose you were to make a class hierarchy of
shapes. How would order: parallelogram, rhombus, rectangle
and square?
Abigail
| [reply] |
|
|
If the answer to a question is "multiple inheritance", you've probably asked the wrong question.
Really? Suppose you were to make a class hierarchy of shapes. How would order: parallelogram, rhombus, rectangle and square?
Poorly.
The problem is that as soon as we add in multiple inheritance, we add tremendous conceptual complexities in how we figure out what to do when we have defined actions in multiple places in our class hierarchy that might reasonably (under different models of how things work) apply. And it isn't that there is a right or wrong way to do this. In different situations, different ways of resolving the same ambiguity make sense. Furthermore no way of handling multiple inheritance that I have seen will always be appropriate.
Furthermore look at your example. You offer 4 basic geometric types. But would you even really want to model them with an isa arrangement? I don't know about you, but when I think of modelling a square, most of the implementations that I would start with don't turn into good implementations of a parallelogram or rhombus. Unless, of course, I started knowing that I wanted parallelograms...
The Structure and Interpretation of Computer Programming has some interesting things to say on this. In Inadequacies of hierarchies they offer a more involved version of your example and dryly note, Dealing with large numbers of interrelated types while still preserving modularity in the design of large systems is very difficult, and is an area of much current research. In the footnotes they expand on this with, Developing a useful, general framework for expressing the relations among different types of entities (what philosophers call ``ontology'') seems intractably difficult. The main difference between the confusion that existed ten years ago and the confusion that exists now is that now a variety of inadequate ontological theories have been embodied in a plethora of correspondingly inadequate programming languages.
This remark seems to me to be dead on. When you decide how to handle multiple inheritance, you are settling on a particular ontological framework. To the extent that your framework does not naturally express the understanding of the programmer, you create confusion. The subtler and more complex the distinctions that you draw, the more readily you will find that slight confusion on the part of the programmer rapidly multiplies into obfuscation, errors, and rapidly escalating design complexity. Particularly when you get to comprehension issues that virtually nobody has decent mental models of.
Do you deny that people lack good mental models of multiple inheritance? Oh, we can all produce diagrams, and for simple geometrical shapes it is easy enough to say what the inheritance diagram should be. But where is the simple diagram and natural examples that explains how traversing the class hierarchy in one order differs from traversing it in another differs from using Class::MultiMethods to locate a method based on both arguments? You know those three are different, I know they are different, and we can both produce code proving that they are different. But I can't provide an understandable model of the difference beyond, "Here are the rules and here is how the rules will apply in these cases."
Therefore I'm inclined towards merlyn's view. If you can solve your problem using single inheritance, do so. The resulting design is likely to be easier to come up with and easier for others to understand than multiple inheritance. You also avoid the potential for several kinds of "gotchas". And when you learn another language, you can design the same way secure in the knowledge that single inheritance always is present, and its behaviour doesn't tend to change that much between languages so you have less relearning to do.
| [reply] |
|
|
Furthermore look at your example. You offer 4 basic geometric types. But would you even really want to model them with an isa arrangement? I don't know about you, but when I think of modelling a square, most of the implementations that I would start with don't turn into good implementations of a parallelogram or rhombus. Unless, of course, I started knowing that I wanted parallelograms...
Yes, I want to model them in an isa arrangement. I wasn't
thinking about "modelling" a square. I was thinking about
a property system. A square has all the properties of a
rectangle. A square has all the properties of a rhombus.
Both the rectangle and the rhombus have all the properties
of a parallelogram. What is more natural than multiple
inheritance to express these facts? See also my response to
flying moose.
Therefore I'm inclined towards merlyn's view. If you can solve your problem using single inheritance, do so.
While I agree with the latter, I don't think that's what
Merlyn said. What he said was:
If the answer to a question is "multiple inheritance", you've probably asked the wrong question.
which to me means "never use multiple inheritance".
And when you learn another language, you can design the same way secure in the knowledge that single inheritance always is present, and its behaviour doesn't tend to change that much between languages so you have less relearning to do.
That I find a weak argument. I'm not shying away from hashes
because normal arrays are always present in other languages
and hashes may not. Besides, there are languages that don't
have support for even single inheritance (for the mere fact
they don't have native OO support).
Abigail
| [reply] |
|
|
|
|
|
|
|
The problem is that as soon as we add in multiple inheritance, we add tremendous conceptual complexities in how we figure out what to do when we have defined actions in multiple places in our class hierarchy that might reasonably (under different models of how things work) apply.
Well, surely its not too difficult to arrange that this doesnt happen? For instance MI as a means to "mix-in" stuff always seemed to me a reasonable preactice.
---
demerphq
First they ignore you, then they laugh at you, then they fight you, then you win.
-- Gandhi
| [reply] [d/l] |
|
|
|
|
|
|
|
| [reply] |
|
|
I think if you have to model the differences in polygons with an object model, you are really taking your object model too far down. The polygon class should be sufficiently advanced to take arbitrary constraints and handle all properties generically. After all, a 71-sided polygon has no name, and we didn't write an object for that yet! Nor should we have to. There shouldn't have to be classes for that, and to a mathematician, rules for a 71-sided polygon with equal angles are scarely different than a quad... the numbers just come out cleaner.
It's like the canonical OO example of cats and dogs where the user writes canine and feline in there, and writes support in there for the future inclusion of fish. It just going to darn far.
Moral of the story -- Inheritance is a powerful weapon in the OO arsenal. It is but one of many weapons, and is the most overused hammer for the proverbial nail. Abuse of inheritance and not knowing when to inherit versus encapsulate objects has led to many coding problems I've seen in my work -- folks make this error in all sorts of languages, because the power of inheritance is very alluring. It takes a lot to hold it in check -- to know when it should not be used.
| [reply] |
|
|
I think if you have to model the differences in polygons with an object model, you are really taking your object model too far down.
I didn't say polygons. I said shapes.
The polygon class should be sufficiently advanced to take arbitrary constraints and handle all properties generically.
Even if I were to be talking about polygons, I still don't
agree with that. If you follow such reasoning all the way
through, you would never subclass. Instead, you would make
you one and only base class more generic.
There shouldn't have to be classes for that, and to a mathematician, rules for a 71-sided polygon with equal angles are scarely different than a quad... the numbers just come out cleaner.
But as I said, I was thinking about shapes, and properties
of shapes. For instance, let's talk about simple symmetries.
All parallelograms have a rotational point
of symmetry (180 degrees around their center). This is not
true for all 71-sided polygons - in fact, 71 being odd, there is no 71-sided polygon that has a 180 degree rotational symmetry. Every rectangle has two lines of
symmetry - the lines dividing opposite sides, and whatever
symmetry a parallelogram has. Every rhombus has two lines
of symmetry, but different lines than the rectangle, namely
the diagonals. And in addition, it has the symmetries a
parallelogram has. Finally, a square has a 90 degree rotational symmetry, a -90 degree rotation symmetry, and the symmetries of
a rectangle, and the symmetries of a rhombus.
So, my question remains, what kind of inheritance tree
would you use? (And keep in mind, the tree is just part of
a bigger picture, there may be hundreds of other shapes as
well. If you want to collapse them into one general class,
you'll have to do a lot of case statements, and you're back
to non-OO programming).
Abigail
| [reply] |
|
|
|
|
|
|
|
|
|
|
|
| [reply] |
|
|
|
|