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 Thu, 31 Mar 2005 12:30:44 GMT
Daniel Fagerstrom wrote:

> Ok, I'll give you a new example trying to explain the concept. Let's 
> start with the portal block and say that I want to use that for my app, 
> MyPortal, but I want a different skin. The skin consists of the 
> stylesheet "styles/portal-page.xsl" among other things. We assume that 
> the designer of the portal block wanted to make the skin overridable so 
> there is a sitemap rule that exposes the stylesheet in the Portal block.
> 
> <match pattern="styles/portal-page.xsl">
>   <read src="default-portal-page.xsl"/>
> </match>
> 
> Now I let MyPortal extends Portal and reimplements the stylesheet in 
> MyPortal
> 
> <match pattern="styles/portal-page.xsl">
>   <read src="my-portal-page.xsl"/>
> </match>
> 
> and thanks to polymorphism the user of MyPortal will get 
> "my-portal-page.xsl" when asking for "styles/portal-page.xsl" and 
> everything else will be delivered from Portal.
> 
> But is that what we want? Taking a closer look at the sitemap in Portal 
> there are a number of sitemap rules that uses "styles/portal-page.xsl" 
> for creating other resources: "portal", "loggedin" and 
> "resources/login-error.xml". What about them, should they use the the 
> stylesheet from Portal or from MyPortal? In general it depends, but in 
> this case our aim was to change to our on skin for the portal so we 
> should use it everywhere.
> 
> This is also covered by the polymorphism concept from OO. A service that 
> is defined and used in the base block but overridden in the extending 
> block should use the overriding service in the base block as well. But 
> IMO it could be confusing if the default behaviour would be to have 
> polymorphic lookup as default in blocks and because of that I proposed a 
> block:polymorph subprotocol for being explicit about what service 
> accesses in a (base) block that are supposed to be overridable. So in 
> the Portal block we could have a sitemap rule:
> 
> <match pattern="portal">
>   ...
>   <transform src="block:polymorph:/styles/portal-page.xsl"/>
>   ...
> </match>
> 
> if we access "portal" from the MyPortal block and have overrided 
> "styles/portal-page.xsl" in MyPortal, the version from MyPortal will be 
> used instead.


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


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

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

SITEMAP.XMAP
<map:match pattern="one-special.css">
   <map:read src="styles/css/one-special.css"/>
</map:match>

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



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