cocoon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Reinhard Poetz <reinh...@apache.org>
Subject Re: [RT] composition vs. inheritance in blocks
Date Fri, 01 Apr 2005 06:25:46 GMT
Daniel Fagerstrom wrote:
> Reinhard Poetz wrote:
> <snip/>
> 
>> As promised, I've tried to come up with some pseudo-code that shows 
>> how "single-inheritance + composition" looks like in a particular 
>> example (portal + skinning).
> 
> 
> That is excelent.
> 
>>                                           - o -
>>
>> We have following block interfaces:
>> ===================================
>>  - http://cocoon.apache.org/interfaces/portal/1.0
>>  - http://cocoon.apache.org/interfaces/portal-skin/1.0
>>  - http://mycompany.com/interfaces/skin/1.0
>>
>>
>> Here the block implementations:
>> ===============================
>>
>> --------------------------------------------------------------------------- 
>>
>> "Portal"
>> --------------------------------------------------------------------------- 
>>
>> BLOCK.XML
>> <block xmlns="http://apache.org/cocoon/blocks/cob/1.0"
>>  id="http://cocoon.apache.org/blocks/portal/1.0.0">
>>   <name>portal</name>
>>   <requirements>
>>     <requires 
>> interface="http://cocoon.apache.org/interface/portal-skin/1.0"
>>      name="portal"
>>      default="http://cocoon.apache.org/blocks/portal-skin/1.0.0"/>
>>   </requirements>
>>   <implements>
>>     <interface id="http://cocoon.apache.org/interfaces/portal/1.0"/>
>>   </implements>
>> </block>
>>
>> --------------------------------------------------------------------------- 
>>
>> "Portal-Skin"
>> --------------------------------------------------------------------------- 
>>
>> BLOCK.XML   <block xmlns="http://apache.org/cocoon/blocks/cob/1.0"
>>  id="http://cocoon.apache.org/blocks/portal-skin/1.0.0">
>>   <name>portal-skin</name>
>>   <implements>
>>     <interface id="http://cocoon.apache.org/interfaces/portal-skin/1.0"/>
>>   </implements>
>> </block>
>>
>> SITEMAP.XMAP
>> <map:match pattern="*.css">
>>   <map:read src="styles/css/{1}.css"/>
>> </map:match>
>> <map:match pattern="styles/portal-page.xsl">
>>   <map:read src="styles/portal-page.xsl"/>
>> </map:match>
>>
>>
>> --------------------------------------------------------------------------- 
>>
>> "MyPortal"
>> --------------------------------------------------------------------------- 
>>
> 
> 
> Is MyPortal an actual application or is it a block that you are going to 
> use for building applications? Let's call them application block and 
> reusable block respectively. We must take application blocks into 
> account to get the whole picture, so I assume that MyPortal is an 
> application block that will contain real user profiles etc. Seeing that 
> it neither implements nor extends but rather use the portal block 
> enforces that view.
> 
>> BLOCK.XML   <block xmlns="http://apache.org/cocoon/blocks/cob/1.0"
>>  id="http://mycompany.com/blocks/my-Portal/1.0.0">
>>   <name>MyPortal</name>
>>   <requirements>
>>     <requires interface="http://mycompany.com/interfaces/skin/1.0"
>>       name="skin"
>>       default="http://mycompany.com/blocks/myskin/1.0.0"/>
>>     <requires interface="http://cocoon.apache.org/interfaces/portal/1.0"
>>       name="portal"
>>       default="http://cocoon.apache.org/blocks/portal/1.0.0"/>
>>   </requirements>
>> </block>
>>
>> SITEMAP.XMAP
>> <map:match pattern="portal">
>>   <map:act type="portal:auth-protect">
>>     <map:parameter name="handler" value="portal-handler"/>
>>     <map:parameter name="application" value="portal"/>
>>
>>     <map:generate type="portal:portal"/>
>>     <map:transform src="blocks://skin/styles/portal-page.xsl">
>>       <map:parameter name="user" value="{ID}"/>
>>     </map:transform>
>>     <map:transform type="core:cinclude"/>
>>     <map:transform type="portal:portal-coplet"/>
>>     <map:transform type="portal:portal-new-eventlink"/>
>>     <map:transform type="core:encodeURL"/>
>>     <map:serialize type="portal:html-include"/>
>>   </map:act>
>> </map:match>
> 
> 
> Shouldn't this sitemap be part of Portal rather than MyPortal, AFAICS it 
> is a "blockified" version of the "portal" rule from the demo portal, so 
> there seem no to be no reason to reimplement it in MyBlock.
> 
>> (Note: Most of the used components come from the portal block, the 
>> rest from Cocon core; the stylesheet is provided by the "skin" block.)
>>
>> --------------------------------------------------------------------------- 
>>
>> "MySkin"
>> --------------------------------------------------------------------------- 
>>
>> BLOCK.XML
>> <block xmlns="http://apache.org/cocoon/blocks/cob/1.0"
>>  id="http://mycompany.com/blocks/my-Portal/1.0.0">
>>   <name>MySkin</name>
>>   <implements>
>>     <interface id="http://cocoon.apache.org/interfaces/portal/1.0"/>
>>   </implements>
>>   <extends>http://cocoon.apache.org/blocks/portal-skin/1.0.0</extends>
>> </block>
> 
> 
> What does extends actually buy us here, couldn't we just use "requrires" 
> for the relation to portal-skin/1.0.0.

IIUC using "extends" was the original idea here.

> 
> <requires block="http://cocoon.apache.org/blocks/portal-skin/1.0.0" 
> name="skin"/>
> 
> and be explicit about what we expose from portal-skin/1.0.0?
> 
>> SITEMAP.XMAP
>> <map:match pattern="one-special.css">
>>   <map:read src="styles/css/one-special.css"/>
>> </map:match>
> 
> 
> Being explicit means that we end the sitemap with:
> 
> <map:mount uri-prefix="" src="blocks://skin"/>
 >
>>                                           - o -
>>
>> The project that wants to use the Portal is in the block "MyPortal". 
>> It needs several components from "Portal" (generator, several 
>> transformers) and it needs a block that provides the skin, or more 
>> precisly, it needs a block that implements 
>> http://cocoon.apache.org/interfaces/portal-skin/1.0. This can either 
>> be "portal-skin", the default skin, or "MySkin", that provides one 
>> additional CSS. Everything else is taken from "portal-skin".
>>
>>
>> From my POV this solution is very clear and comprehensible. The 
>> aspects "portal functionality" and "skinning" are separated and the 
>> used implementations can simply be replaced by other implementations 
>> (shown by using the "mySkin" block).
>>
>>                                           - o -
>>
>> What does your solution that ueses multiple inheritance look like? (If 
>> this is a bad example to show the advantages of MI feel free to 
>> enhance it!)
> 
> 
> First I will not use the term MI as it doesn't describe what I want to 
> achieve that well and as it also seem to stir all kinds of bad reactions 
> that distracts us from the task at hand.
> 
> So I agree with most of what you show in your example, it looks neat. 
> What I lack from it is how to reuse the sitemap in the Portal block.
> 
> I would have a sitemap similar to the one in the demo portal in the 
> Portal block. But e.g. the profiles part in the portal-handler 
> configuration would rather be:
> 
> Portal Sitemap
> --------------
> 
> ...
> <profiles>
>  <copletbasedata-load 
> uri="blocks:/load-global-profile?profile=copletbasedata"/>
>  <copletdata-global-load 
> uri="blocks:/load-global-profile?profile=copletdata"/>
>  <copletdata-role-load uri="blocks:/load-role-profile?profile=copletdata"/>
>  <copletdata-user-load uri="blocks:/load-user-profile?profile=copletdata"/>
>  <copletinstancedata-global-load 
> uri="blocks:/load-global-profile?profile=copletinstancedata"/>
>  <copletinstancedata-role-load 
> uri="blocks:/load-role-profile?profile=copletinstancedata"/>
>  <copletinstancedata-user-load 
> uri="blocks:/load-user-profile?profile=copletinstancedata"/>
>  <copletinstancedata-user-save 
> uri="blocks:/save-user-profile?profile=copletinstancedata"/>
>  <layout-global-load uri="blocks:/load-global-profile?profile=layout"/>
>  <layout-role-load uri="blocks:/load-role-profile?profile=layout"/>
>  <layout-user-load uri="blocks:/load-user-profile?profile=layout"/>
>  <layout-user-save uri="blocks:/save-user-profile?profile=layout"/>
> </profiles>
> ...
> 
> Meaning that the different configuration pipelines are found through the 
> blocks manager that would ask the extending block (recursively) for the 
> configuration pipelines first, and if they not are found there, the own 
> pipeline would be used.
> 
> Then MyPortal could redefine some of the configuration pipelines and 
> reuse the rest from Portal:
> 
> MyPortal Sitemap
> ----------------
> 
> ...
> <pipeline>
>  <match pattern="load-user-profile">
>    ...
>  </match>
> 
>  <mount uri-prefix="" src="blocks://portal"/>
> </pipline>
> ...
> 
> Now this mechanism is more limited than real inheritance. map:mount 
> become a two way contract where the mounting sitemap can be asked about 
> services through the block manager, but it doesn't export the interface 
> of the "extended" block. If we have something like the we probably 
> should have some way to differ between mounts that allow the mounted 
> block to ask and those who don't.


(I think) I understand what you want. You called it an "application block" which 
is the base for all your applications. This application block (e.g. a company 
portal) provides services that can be used and customized by other blocks.

>                                  --- o0o ---
> 
> Concerning the skin I find it somewhat burocratic to need to define a 
> new block for beeing able to extend it but I'm ok with it for the time 
> beeing, we will see when we start to use the things. What I would prefer 
> would be to do something like:
> 
> MyPortal Sitemap
> ----------------
> 
> ...
> <pipeline>
>  <match pattern="load-user-profile">
>    ...
>  </match>
> 
>  <match pattern="skin/one-special.css">
>    <read src="styles/css/one-special.css"/>
>   </match>
> 
>  <mount uri-prefix="skin" src="blocks://skin"/>
> 
>  <mount uri-prefix="" src="blocks://portal"/>
> </pipline>
> ...
> 
>                                  --- o0o ---
> 
> So what do you think about this?

I think I got the idea. Personally, I would solve this by composition; the 
profiling is just another reference of the block. Of course, as Stefano said, 
blocks have to be designed for this. If the application block hasn't factored 
out the profile aspect, then you can't replace implementation A by implementation B:

---------------------------------------------------------------------------
"Application block" (to be reused in many projects)
---------------------------------------------------------------------------
<block xmlns="http://apache.org/cocoon/blocks/cob/1.0"
  id="http://mycompany.com/blocks/application-block/1.0.0">
   <name>Application block</name>
   <requirements>
     <requires interface="http://mycompany.com/interfaces/skin/1.0"
       name="skin"
       default="http://mycompany.com/blocks/myskin/1.0.0"/>
     <requires interface="http://cocoon.apache.org/interfaces/profile/1.0"
       name="profile"
       default="http://cocoon.apache.org/blocks/portal/1.0.0"/>
     <requires interface="http://cocoon.apache.org/interfaces/portal/1.0"
       name="portal"
       default="http://cocoon.apache.org/blocks/portal/1.0.0"/>
   </requirements>
</block>


What I do like is <mount uri-prefix="" src="blocks://portal"/> which makes it 
explicit what a block exports. I'm not sure about why this has to be a "two way 
contract".
If you write <copletdata-role-load 
uri="blocks:/load-role-profile?profile=copletdata"/> then it means that you make 
  it explicit what can be overriden and what not.
I would use <copletdata-role-load 
uri="blocks:/profile/load-role-profile?profile=copletdata"/> which requires 
another reference. Using the proposed <mount>-mechanism, you can reuse the 
"portal"-pipeline.

                                        - o -

I think for now we shoudn't support these two-way contracts but favour 
composition by references (incl. <map:mount uri="[another-block]"/>). If this 
gets too bureaucratic, we can still think about alternatives.

WDYT?

-- 
Reinhard Pötz           Independent Consultant, Trainer & (IT)-Coach 

{Software Engineering, Open Source, Web Applications, Apache Cocoon}

                                        web(log): http://www.poetz.cc
--------------------------------------------------------------------


Mime
View raw message