JicarillaWiki   IocContainerInternals/TheBasics/TheSmartHashMap UserPreferences
 
HelpContents Search Diffs Info Edit Subscribe XML Print View

This wiki is in 'slumber' mode, just like its associated sourceforge project. Edits are disabled, the content is potentially stale and is not maintained. That said, it contains some really useful stuff still. Enjoy!


Contents | Previous | Next

The Smart HashMap

(part of the IocContainerInternals paper)

What just happened?

Okay, so we got a taste of how to decorate the basic Map interface to do some useful IoC-container-style stuff for us on the previous page. By introducing a support class to our rather trivial example, we were able to remove some concerns from our main piece of code. We moved the following responsibilities to the container:

  1. Holding our components. Both their definitions (where a definition for us was a class plus an associated name), and their instances (of which we only directly retrieved the JFrame, we didn't even care about the button instances).
  2. Creating our components. We removed all our "new XXXThingie()" statements, remember?
  3. Wiring our components together. We made the container responsible for adding the buttons to the frame.

The fundamental concerns

What we've done, in fact, is seperate out concerns. Let us identify a few of the concerns present in our trivial example:

  1. interacting with the JVM and the command line (fancy term: container embedding)
  2. declaring the components to use (fancy term: assembly)
  3. creating components (fancy term: creating components)
  4. managing components (fancy term: can't think of any right now)
  5. wiring components together (fancy term: dependency resolution)
  6. application logic (fancy term: domain modelling decomposition and modelling)

the funny thing is ("Stop looking at that squirrel!"), each and every one of these concerns is present in each and every java application. Items 1,3,4 and 5 seem like good candidates for writing once, then re-using everywhere. Item 2 is of course specific to an application (what a componentized application does is determined by what components you pick and how they interact). With item 6...well, some components (which embody our application logic) will be re-used across many projects (like the JButton we used), and some will be application-specific.

The challenge

Build something that can handle item 1,3,4 and 5 for each and every software application.

Meeting that challenge

We know how to do this for some things. Others are more difficult. We'll start simple.

Creating Components

For example, we know that if we want to isolate component creation from the component itself, what we use is a Factory. Factories are used all over the place. Something like this

class JButtonFactory 
{ 
  JButton getInstance() 
  { 
    return new JButton(); 
  } 
 
  JButton getInstance( String caption ) 
  { 
    return new JButton( caption ); 
  } 
} 

may not seem very powerful (the getInstance() method just wraps a constructor), but if you make things a little more generic, you can do all kinds of things. I won't talk about this very much here (its called the factory pattern if you need to google it up), but lets just go for a real simple and generic factory in our examples:

public interface Factory 
{ 
  Object getInstance(); 
} 

Interacting with the JVM

I lied a moment ago. I said we wanted to solve this one once for each and every application. The truth is we want to solve it several times. We want to do this for commandline programs with a main() method, but we also want to do it for web applications (deployed inside a servlet engine), enterprise applications (deployed inside a J2EE server), applets (deployed inside a browser), and other applications.

Okay, so let's recognize that we need several environments in which to embed the container. The container needs high embeddability. Let's leave it at that for now.

Wiring components together

This is the really interesting part! Martin Fowler wrote [WWW]a nice piece about IoC component wiring recently, so I can be brief. If I have the simpson family, I might manually wire them together like this:

    public Marge marge = new MargeImpl(); 
    public Homer homer = new HomerImpl( marge ); 
    public Bart bart = new BartImpl( homer, marge ); 
    public Lisa lisa = new LisaImpl( homer, marge ); 
    public Maggie maggie = new MaggieImpl( homer, marge ); 

one of the big things IoC containers do for you is taking this work out of your hands. Consider this bit of pseudocode:

  function assemble() 
  { 
    add( Bart ) 
    add( Homer ) 
    add( Lisa ) 
    add( Maggie ) 
    add( Marge ) 
  } 
  function wire() { perform-equivalent-of-previous-sample-here() } 
 
  assemble() 
  wire() 
  getMarge().frown() 

this kind of "delegated wiring" might not seem like a huge advantage above doing the wiring manually as in the first example, but once you have dozens of components with much more complex interdependencies, changing interdependencies, and many places where you call the constructors, it really is. This is such a big timesaver. And it is quite possible. I'm not going to show you all the code needed to do the above (it requires some smart reflection magic), but here's a (simplified from actual code) snippet:

  public Object[] getDependenciesFor( Class clazz ) 
    { 
        Constructor[] constructors = clazz.getConstructors(); 
        Constructor constructor = findTheRightConstructor( constructors ); 
        Class[] parameterTypes = constructor.getParameterTypes(); 
        Object[] parameterValues = new Object[parameterTypes.length]; 
 
        for( int i = 0; i < parameterTypes.length; i++ ) 
        { 
            Class parameterType = parameterTypes[i]; 
            Object parameterValue = getParameterValue( parameterType ); 
            parameterValues[i] = parameterValue; 
        } 
 
        return parameterValues; 
    } 

Hopefully you can imagine how reflection utilities like this can be used inside a container. Something like

  public void add( Object key, Class clazz ) 
  { 
    m_classes.put( key, clazz ) 
    Object constructorArgs = getDependenciesFor( clazz ); 
    Object instance = getInstance( clazz, constructorArgs ); 
    m_instances.put( key, instance ); 
  } 
  public Object get( Object key ) 
  { 
    return m_instances.get( key ); 
  } 

Managing components

This may sound like a big term, but the little bit of sample code above already does component management: it keeps track of the component declaration (m_classes.put( key, args ), and also of the instances of those components m_instances.put( key, args ). Full-featured containers may have options for allowing multiple instances of a declaration, for associating an instance of a declaration with each client, for removing components, etc etc.

Contracts

Okay, so it seems possible to really provide a usable generic way to handle at least some of these responsibilities. Sweet! But in order for all this to work, we really do need to be real clear about some contracts


Okay, this page is getting long, and I'm really getting carried away with talk about contracts, definitions, responsibilities, and fancy terms for all kinds of different things. You've had enough of a taste of all this I guess. I think I need another beer. See you on the next page.


Contents | Previous | Next
PythonPowered
EditText of this page (last modified 2007-02-18 14:14:22)
FindPage by browsing, title search , text search or an index
Or try one of these actions: AttachFile, LikePages, LocalSiteMap, SpellCheck

Creative Commons License
The contents of this wiki are licensed under a Creative Commons License.