| IocContainerInternals/TheBasics/TheSmartHashMap |
UserPreferences |
| JicarillaWiki | FrontPage | Overview | Articles | GetStarted | Sourceforge Project Page | RecentChanges |
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!
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:
What we've done, in fact, is seperate out concerns. Let us identify a few of the concerns present in our trivial example:
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.
Build something that can handle item 1,3,4 and 5 for each and every software application.
We know how to do this for some things. Others are more difficult. We'll start simple.
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();
}
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.
This is the really interesting part! Martin Fowler wrote
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 );
}
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.
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