cocoon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Stefano Mazzocchi <stef...@apache.org>
Subject [RT] composition vs. inheritance in blocks
Date Tue, 29 Mar 2005 16:22:35 GMT
Daniel Fagerstrom wrote:

> But if we step up from the technical details, the main reason that I 
> want multiple inheritance is that I want to make it easy to build 
> webapps by extending and partly overiding a couple of orthogonal blocks. 
> When you build your webapp block it might extend e.g. a Forrest block 
> for documentation structure and skinning, a Lenya block for CMS 
> functionality a user handling block for user adminstration etc. If you 
> use extension you get a default behaviour from the beginning and then 
> you can a step at the time overide the resources you want to modify.
> 
> If you use a block instead of extending it, it is much more complicated 
> to modify its behaviour. Take a look at Forrest, e.g. To make it 
> extendible you have to use all kinds of global variables to simulate 
> polymorphism. You must explicitly tell where it should search for 
> different resources in your Forrest conf. As I have said before, I have 
> practical experience of building webapps based on sitemap polymorphism 
> and multiple inheritance, and it is much more convenient than having 
> Forrest like conigurations for every block you want to extend.
> 
> <snip/>
> 
>>>> ------------------------------------------------------------------------------

>>>>
>>>> Improvement #3: block inheritance
>>>>
>>>> The third step is to allow blocks to extends other blocks.
>>>>
>>>> The idea is to be able to wrap a block with another one, creating an 
>>>> 'overloading' mechanism similar to the one used by OOP inheritance 
>>>> where methods are 'fall back' to the extended class if the extending 
>>>> class doesn't implement them.
>>>>
>>>> Let us supposed we have the following block (very simple):
>>>>
>>>> block "A" implements "skin"
>>>>
>>>>          /stylesheets/changes2document.xslt
>>>>          /stylesheets/faq2document.xslt
>>>>          /stylesheets/document2html.xslt
>>>>          /resources/logo.gif
>>>>
>>>> and let us suppose that we want to change the look and feel of that 
>>>> block. The first two stylesheets provide simply a way to adapt from 
>>>> more specific markup to the Document DTD. So, my block would need to 
>>>> change only the last two resources 'document2html.xslt' and 'logo.gif'.
>>>>
>>>> The best solution is to allow my block to explicitly "extend" that 
>>>> block and inherits the resources that it doesn't contain.
>>>>
>>>> block "B" extends block "A"
>>>>
>>>>          /stylesheets/document2html.xslt
>>>>          /resources/logo.gif
>>>>
>>>> but then block B still is considered implementing behavior "skin" 
>>>> because the rest is inherited.
>>>>
>>>> This mainly:
>>>>
>>>>     * reduces block development and maintanance costs because 
>>>> changes and bugfixes are directly inherited by all the extending 
>>>> blocks, thus allowing better SoC between the two groups mainaining 
>>>> the different blocks
>>>>
>>>>     * easy customization: blocks can be adapted for personal 
>>>> specific needs simply with a wrapper around and without the need to 
>>>> repackaging.
>>>
>>>
>>>
>>>
>>> Ok, allready discussed that above. I think we need to be more 
>>> explicit about what behaviour we want. If we just write:
>>>
>>> <map:transform src="stylesheets/document2html.xslt"/>
>>>
>>> that means normally the same as:
>>>
>>> <map:transform src="context:/stylesheets/document2html.xslt"/>
>>>
>>> and I don't think it is a good idea for isolation between block to be 
>>> able to overide what is in the current context, only things that are 
>>> exposed through the sitemap should IMO be overidable:
>>>
>>> <map:transform src="block:polymorph:/stylesheets/document2html.xslt"/>.
>>
>>
>>
>> I'm not sure if Stefano and you mean the same here. IIUC in Stefano's 
>> example
>> you have e.g. blockA and blockB. blockA implements the behaviour skin and
>> extends blockB:
>>
>>
>> <block id="http://cocoon.apache.org/blocks/A/1.0.0">
>>  <extends>
>>   <block id="http://cocoon.apache.org/interface/B/1.0.0"/>
>>  </extends>
>>  <implements>
>>    <interface id="http://cocoon.apache.org/interface/skin/1.0"/>
>>  </implements>
>> </block>
>>
>> A sitemap snippet of blockA:
>>
>> <map:match pattern="stylesheets/document2html.xslt">
>>   <map:read src="{0}"/>
>> </map:match>
>>
>> A sitemap snippet of blockB:
>>
>> <map:match pattern="stylesheets/changes2.html.xslt">
>>   <map:read src="{0}"/>
>> </map:match>
>> <map:match pattern="stylesheets/document2html.xslt">
>>   <map:read src="{0}"/>
>> </map:match>
>>
>>
>> Another block, e.g. blockC now uses blockA for its styling:
>>
>> <block id="http://cocoon.apache.org/blocks/C/1.0.0">
>>  <requirements>
>>   <requires block="http://cocoon.apache.org/interface/skin/1.0"
>>    default="http://cocoon.apache.org/blocks/A/1.0.0"
>>    name="skin"/>
>>  </requirements>
>> </block>
>>
>> Here a sitemap snippet of blockC:
>>
>> <map:match pattern="myPipeline1">
>>   <map:generate src="bla"/>
>>   <map:transform src="blocks:skin:/stylesheets/document2html.xslt"/>
>>   <map:serialize/>
>> </map:match>
>> <map:match pattern="myPipeline2">
>>   <map:generate src="bla"/>
>>   <map:transform src="blocks:skin:/stylesheets/changes2html.xslt"/>
>>   <map:serialize/>
>> </map:match>
>>
>> myPipeline1 gets the stylesheet from blockA and myPipeline2 from 
>> blockB (no
>> stylesheet available in blockA --> fallback to super block blockB)
>>
>> Does this make sense for you?
> 
> 
> It does.
> 
>> How does block:polymorph apply in this usecase?
> 
> 
> In your example block A extends block B, then in block B we can have a 
> sitemap rule like:
> 
> <map:match pattern="**.html">
>  <map:generate src="{1}.xml"/>
>  <map:transform src="block:polymorph:/stylesheets/document2html.xslt"/>
>  <map:serialize/>
> </map:match>
> 
> The block:polymorphism makes that stylesheets/document2html.xslt is 
> taken from A rather than B. Say that you use that stylesheet in several 
> rules, then you change multiple behaviours by overriding it.
> 
> 
>>>> ---------------------------------------------------------------------------------

>>>>
>>>>
>>>> I'm not sure about multiple-block inheritance. For me it's some kind 
>>>> of an anti-pattern but maybe I'm too Java-minded.
>>>
>>>
>>> Multiple inheritance is not an antipattern in itself. The idea that 
>>> it should be is IMO mainly marketing BS from Sun to make the stupid 
>>> idea of single implementation inheritance seem like a feature.
>>>
>>> But there are a number of antipattern that was popular in OO maybe 
>>> 10-20 years ago that was based on multiple inheritance. But that is a 
>>> different thing. People tended to model the world with classes 
>>> instead of interfaces, and used deep an multiple inheritance that got 
>>> a lot of garbage from all the to full featured base classes. Also as 
>>> the extension relation in Java (and many other OO languages) is much 
>>> "harder" than using through interface, extension was considered less 
>>> flexible.
>>>
>>> But as discussed above we don't need extension to be as hardcoded as 
>>> in OO languages. And also implementation inheritance is a rather 
>>> natural and efficient way to put things together.
>>
>>
>> I think that modelling applications using interfaces and implementing 
>> them using
>> delegation is better than extension ...
> 
> 
> If you want to convince me I'm afraid that you have to tell why you 
> think it as well ;)
> 
> Using extension as it happen to be implemented in C++ and Java can be an 
> anti pattern under certain circumstances. But saying that something is 
> an antipattern without giving a context is close to meaningsless. For 
> blocks extension give us polymorphism and if we want to extend and 
> override several different concern areas in our webapp, we need multiple 
> inheritance.
> 
> If we instead using interfaces they are for the moment just an URI an 
> maybe some documentation. And for the delegation, it means that you have 
> to explicitly code the usage pattern for every block service.
> 
>> The question for blocks now is what
>> multi-block inheritance really buys us.
> 
> 
> Extend and override from multiple blocks based on polymorphism.
> 
>> As I said above I think we should go
>> through all 3 block services (pipelines, components, flows) and describe
>> scenarios and compare pros and cons of multi-block inheritance 
>> compared to single-block inheritance.
> 
> 
> The pros with multi-block inheritance is that your block can be vertical 
> frameworks with default behaviour that you can modify by overiding 
> certain pipeline rules, components and flow functions. The cons is that 
> we have to spend some thinking to get the design right and to get the 
> behaviour intuitive enough.
> 
> Single block inheritance is possibly good in the way that it is easy to 
> see where you get things from and it is bad in that you have to do a lot 
> of coding if you want to use several vertical frameworks that takes care 
> of different concern areas in your webapp.
> 
> Frankly I can't see much advantages in single implementation inheritance 
> at all. If delegation always is better we should only have interface 
> inheritance and not even single implementation inheritance. And if 
> implementation inheritance is good, and for webapps I'm quite certain 
> that it is, it seem like a very arbitrary limitation to just allow for 
> inheriting implementation for one concern area.

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 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.

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.

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.

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!

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.

If you *don't* care for reusability, then it's true that multiple 
implementation inheritnace can serve as a cheaper form of composition.

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).

-- 
Stefano.


Mime
View raw message