tapestry-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From conflue...@apache.org
Subject [CONF] Apache Tapestry > IoC cookbook - patterns
Date Thu, 25 Nov 2010 12:00:00 GMT
<html>
<head>
    <base href="https://cwiki.apache.org/confluence">
            <link rel="stylesheet" href="/confluence/s/1810/9/12/_/styles/combined.css?spaceKey=TAPESTRY&amp;forWysiwyg=true"
type="text/css">
    </head>
<body style="background: white;" bgcolor="white" class="email-body">
<div id="pageContent">
<div id="notificationFormat">
<div class="wiki-content">
<div class="email">
    <h2><a href="https://cwiki.apache.org/confluence/display/TAPESTRY/IoC+cookbook+-+patterns">IoC
cookbook - patterns</a></h2>
    <h4>Page <b>edited</b> by             <a href="https://cwiki.apache.org/confluence/display/~ccordenier">Christophe
Cordenier</a>
    </h4>
        <div id="versionComment">
        <b>Comment:</b>
        Fix broken links<br />
    </div>
        <br/>
                         <h4>Changes (1)</h4>
                                 
    
<div id="page-diffs">
            <table class="diff" cellpadding="0" cellspacing="0">
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >h1. Chain of Command Pattern <br>
<br></td></tr>
            <tr><td class="diff-changed-lines" >Let&amp;apos;s look at another
example, again from the Tapestry code base. The <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">[InjectProvider|../../apidocs/org/apache/tapestry5/services/InjectionProvider.html]</span>
<span class="diff-added-words"style="background-color: #dfd;">[InjectProvider|http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/InjectionProvider.html]</span>
interface is used to process the @Inject annotation on the fields of a Tapestry page or component.
Many different instances are combined together to form a [chain of <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">command|../command.html].</span>
<span class="diff-added-words"style="background-color: #dfd;">command|IoC - command].</span>
<br></td></tr>
            <tr><td class="diff-unchanged" > <br>The interface has only
a single method (this is far from uncommon): <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
        </table>
</div>                            <h4>Full Content</h4>
                    <div class="notificationGreySide">
        <h1><a name="IoCcookbook-patterns-UsingPatterns"></a>Using Patterns</h1>

<p>Tapestry IoC has support for implementing several of the <a href="http://en.wikipedia.org/wiki/Design_pattern_(computer_science)"
class="external-link" rel="nofollow">Gang Of Four Design Patterns</a>. In fact, the
IoC container itself is a pumped up version of the Factory pattern.</p>

<p>The basis for these patterns is often the use of <em>service builder methods</em>,
where a <a href="#IoCcookbook-patterns-servconf.html">configuration</a> for the
service is combined with a factory to produce the service implementation on the fly.</p>

<h1><a name="IoCcookbook-patterns-ChainofCommandPattern"></a>Chain of Command
Pattern</h1>

<p>Let&apos;s look at another example, again from the Tapestry code base. The <a
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/InjectionProvider.html"
class="external-link" rel="nofollow">InjectProvider</a> interface is used to process
the @Inject annotation on the fields of a Tapestry page or component. Many different instances
are combined together to form a <a href="/confluence/display/TAPESTRY/IoC+-+command" title="IoC
- command">chain of command</a>.</p>

<p>The interface has only a single method (this is far from uncommon):</p>

<div class="error"><span class="error">Unknown macro: {code|borderStyle=solid}</span>

<p>public interface InjectionProvider</p>
<div class="error"><span class="error">Unknown macro: {
  boolean provideInjection(String fieldName, Class fieldType, ObjectLocator locator,
      ClassTransformation transformation, MutableComponentModel componentModel);
}</span> </div><div class="code panel" style="border-width: 1px;"><div
class="codeContent panelContent">
<pre class="code-java"></pre>
</div></div>

<p>The return type indicates whether the provider was able to do something. For example,
the AssetInjectionProvider checks to see if there&apos;s an @Path annotation on the field,
and if so, converts the path to an asset, works with the ClassTransformation object to implement
injection, and returns true to indicate success. Returns true terminates the chain early,
and that true value is ultimately returned to the caller.</p>

<p>In other cases, it returns false and the chain of command continues down to the next
provider. If no provider is capable of handling the injection, then the value false is ultimately
returned.</p>

<p>The InjectionProvider service is built up via contributions. These are the contributions
from the TapestryModule:</p>
</div>
<p>public static void contributeInjectionProvider(<br/>
    OrderedConfiguration&lt;InjectionProvider&gt; configuration,<br/>
    MasterObjectProvider masterObjectProvider,<br/>
    ObjectLocator locator,<br/>
    SymbolSource symbolSource,<br/>
    AssetSource assetSource)</p>
<div class="error"><span class="error">Unknown macro: {
  configuration.add(&quot;Default&quot;, new DefaultInjectionProvider(masterObjectProvider,
locator));

  configuration.add(&quot;ComponentResources&quot;, new ComponentResourcesInjectionProvider());

  configuration.add(
      &quot;CommonResources&quot;,
      new CommonResourcesInjectionProvider(),
      &quot;after}</span> </div><div class="code panel" style="border-width:
1px;"><div class="codeContent panelContent">
<pre class="code-java">

And, of course, other contributions could be made in other modules ... <span class="code-keyword">if</span>
you wanted to add in your own form of injection.

The configuration is converted into a service via a service builder method:

{code|borderStyle=solid}
  <span class="code-keyword">public</span> InjectionProvider build(List&amp;lt;InjectionProvider&amp;gt;
configuration, ChainBuilder chainBuilder)
  {
    <span class="code-keyword">return</span> chainBuilder.build(InjectionProvider.class,
configuration);
  }</pre>
</div></div>

<p>Now, let&apos;s see how this is used. The InjectWorker class looks for fields
with the InjectAnnotation, and uses the chain of command to inject the appropriate value.
However, to InjectWorker, there is no chain ... just a <em>single</em> object
that implements the InjectionProvider interface.</p>

<div class="error"><span class="error">Unknown macro: {code|borderStyle=solid}</span>
</div>
<p>public class InjectWorker implements ComponentClassTransformWorker<br/>
{<br/>
  private final ObjectLocator locator;</p>

<p>  // Really, a chain of command</p>

<p>  private final InjectionProvider injectionProvider;</p>

<p>  public InjectWorker(ObjectLocator locator, InjectionProvider injectionProvider)</p>
  <div class="error"><span class="error">Unknown macro: {
    this.locator = locator;
    this.injectionProvider = injectionProvider;
  }</span> </div>

<p>  public final void transform(ClassTransformation transformation, MutableComponentModel
model)<br/>
  {<br/>
    for (String fieldName : transformation.findFieldsWithAnnotation(Inject.class))<br/>
    {<br/>
      Inject annotation = transformation.getFieldAnnotation(fieldName, Inject.class);</p>

<p>      try</p>
      <div class="error"><span class="error">Unknown macro: {
        String fieldType = transformation.getFieldType(fieldName);

        Class type = transformation.toClass(fieldType);

        boolean success = injectionProvider.provideInjection(
            fieldName,
            type,
            locator,
            transformation,
            model);

        if (success) transformation.claimField(fieldName, annotation);
      }</span> </div>
<p>      catch (RuntimeException ex)</p>
      <div class="error"><span class="error">Unknown macro: {
        throw new RuntimeException(ServicesMessages.fieldInjectionError(transformation
            .getClassName(), fieldName, ex), ex);
      }</span> </div>

<p>    }<br/>
  }<br/>
}</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java"></pre>
</div></div>

<p>Reducing the chain to a single object vastly simplifies the code: we&apos;ve
<em>factored out</em> the loop implicit in the chain of command. That eliminates
a lot of code, and that&apos;s less code to test, and fewer paths through InjectWorker,
which lowers its complexity further. We don&apos;t have to test the cases where the list
of injection providers is empty, or consists of only a single object, or where it&apos;s
the third object in that returns true: it looks like a single object, it acts like a single
object ... but its implementation uses many objects.</p>

    </div>
        <div id="commentsSection" class="wiki-content pageSection">
        <div style="float: right;">
            <a href="https://cwiki.apache.org/confluence/users/viewnotifications.action"
class="grey">Change Notification Preferences</a>
        </div>
        <a href="https://cwiki.apache.org/confluence/display/TAPESTRY/IoC+cookbook+-+patterns">View
Online</a>
        |
        <a href="https://cwiki.apache.org/confluence/pages/diffpagesbyversion.action?pageId=23338499&revisedVersion=2&originalVersion=1">View
Changes</a>
            </div>
</div>
</div>
</div>
</div>
</body>
</html>

Mime
View raw message