Stefano Mazzocchi wrote:
Daniel Fagerstrom wrote:
<snip/>
I don't like multiple inheritance and this is not just because I learned OOP thru java, but because every single time I wish I had multiple implementation inheritance in java I found a way to go around the problem with single implementation inheritance that was more elegant.
I learned OOP thru C++, so I even have had the possibility of using multiple implementation inheritance, not just the work arounds ;) And in most cases composition is better than multiple implementation inheritance, but in the cases where you merge two different concern areas it is useful and natural according to my experience. While I don't find the "extend the largest abstract base class and repeat the interface and delgate to the other" ideom used in Java particulary elegant.
But, although I believe that we should use well known concepts from OO as much as possible in the design of blocks, as we know some of the consequnces of them and as they are familiar to the users, we must be aware about that blocks and typical objects have quite different levels of granularity and are not completely analogous.
--- o0o ---
So what I propose is that "extend and override" is a convenient and productive way to build webapps. You start from a working default application and add your own data and modify the behaviours that you need to modify. If you have used Forrest, you know what I'm talking about. But the difference between Forrest and what I propose is that you don't need any complicated sitemap tricks and global configurations with search paths, if we makes the block polymorphic.
--- o0o ---
This is not just armchair speculations from me. We have built a number of webapps during the last year based on sitemap polymorphism. The mechanism is rather simple, in the sitemap of the base application we use a variant of the cocoon protocol:
cocoon:polymorph:/foo.xml
that basically calls
cocoon://<sitemap-path>/foo.xml
by mounting the base application in the end of the "extending" sitemap, the polymorph sitemap rules can be overrided just by defining the uri in the before the mount.
Its easy and works well. Of course one have to do some thinking to build a reusable base application, but that goes generally for reusability.
We started to use this pattern because we had a webapp, that a number of customers wanted slight variations of. In the beging we just made the new webapps extend a slight abstraction of the original webapp. Not because I thought it was a good idea, but more because of time constraints. Our experience is, hardly supprising, that it is more flexible to base your webapp on a number of orthogonal vertical frameworks. So we have factored out some such from the original webapp.
--- o0o ---
So our experience is: * extend and override is productive * vertical frameworks are easier to reuse
If you are talking about
block A extends block B
then the functionality that you describe above is already included in the original real block design and you don't even need the (ugly!) polymorphic protocol flavor, since the block protocol should takes care of all this transparently (thru the block dependency map of the block manager).
But (as usual), you jumped directly from that to
block A extends block b *and* c
which, to me, is not the painless jump that you (and Peter) seem to suggest.
This implies AFAICS multiple inheritance
not at all. you can have block 'cascading'/'wrapping' (you name it) without multiple inheritance.
as you have to implement polymorphism by hand if you use composition.
No. Absolutely not. Go back and read all my emails about the block protocol: the functionality you describe is already there and doesn't require MI at all.
If you have better ideas about how to achive default behaviour that is easy to override, I'm of course interested to discus them instead.
see above.
We have only used polymorphism on sitemap level, if it is usefull for components is another question, I would assume that, but it needs further thinking.
exactly.
I would go a little further and say that I believe inheritance is useful only when used as a 'cascading' mechanism, in any other sense is harmful: composition should be used instead.
Why so?
It's extremely hard to design for inheritance, a lot easier to design for composition.
These are different concerns. Of course we should support composition, no doubt about that, but based on the reasons and experiences described above I want inheritance as well.
I completely agree that inheritance is useful. Dead useful. But single inheritance not multiple one.
And it is no doubt easier to design components than vertical frameworks, but that doesn't mean that we should avoid designing framewoks.
hell no. I had enough 'build the framework before we even know where the problem is' avalonish crap in my life, thanks.
Until somebody proves me that this is no way to obtain a required functionality without MI, I'm -1 on it.
And performing composition thru multiple-inheritance is a really terrible way to do it. Why? because you need to go very deep in describing what behaviors get exposed and what are protected, otherwise things get messy very fast.
Sure, it is well known deep hierarchies of implementation inheritance often leads to problems. People tried to use it as "the golden hammer" once, but that doesn't make it less usefull for what it is good for.
Sure, for cascading. Agreed. therefore 'single' inheritance.
Also, multiple-inheritance stops further composition: you seem to suggest that a block that inherit multiply is a 'leaf block', one that is your own application and that will not be used by others.
Yes, that is correct, I want it to be as simple as possible to put together a webapp based on ready made vertical frameworks.
and can you tell me why MI is supposed to help you achieving this?
Well, one of the reasons for block design was to sparkle the creation of reusable application components without people to think much about them: multiple-inheritance yields horizontal behavior changes to the inherited components, which means that if I have block A inherit block B and then block C that wants to use A but was already using B, but A modified B's behavior a little with the inheritance, you have a problem!
Sure, you have to keep your blocks orthogonal to make them reusable, overlapping behaviour leads to a mess.
Bingo.
But honestly, reusabillity has been considered a major topic in the software business for a number of decades. We all know that it doesn't happen spontaniously, you have to design for it and you have to reuse your component or framework a number of times and make lot of improvements before it becomes more generally reusable.
Very very true, and my argument is that MI moves the pain one step down, so that you never go thru exactly *that* design for reuse phase that composition forces you to go thru.
I am strongly against multiple implementation inheritance for blocks, because what you want to do, if you care about reusability, is really multiple composition and that is achieved with being allowed to implement multiple behavioral interfaces.
Of course I want to support that as well.
Very well, then we have composition (multiple implementation of interfaces) and single inherit
If you *don't* care for reusability, then it's true that multiple implementation inheritnace can serve as a cheaper form of composition.
For the majority of Cocoon users I would assume that blocks that are possible to extend and override is easier to reuse (considered that they have a good design of course), than just a lump of components and stylesheets.
But if I had to pick between improving block reusability or ease of composition, I would go with the first, hoping that tools/syntax-sugar would help the second (as it happened with Java).
It's not either or. It's not exactly rocket science to build a mechanism for mutiple inheritance so we can have booth.
We just need to discuss what we want to achieve and how to achive it to get it right.
/Daniel
-- Stefano.
