Now for something a little more complex. Imagine you're writing an application that both provides and uses several "system services", or components, for example a database, some caching, network services, etc. Here, since those are system resources, we have to make sure that only a maximum of one instance of the class that represents a component is instantiated and used.
Let's say you write one component that make use of one of those services, SQLDataStore. When you want to get something from the SQLDataStore in your component/class MyService, you could get away by using the Factory and Singleton pattern by simply calling some static function in the class SQLDataStore. Here's the constructor of the MyService class:
public MyService() { //... SQLDataStore store = SQLDataStore.Factory.getSQLDataStore(); }
Well, if you want to replace the use of SQLDataStore with XMLDataStore, then you'll have to change code in all your components that used SQLDataStore. You could say: "I'll just define an interface called DataStore, then make a generic Factory that returns either an SQLDataStore or XMLDataStore depending on some system settings!" Still, that setting would be global, so it would be difficult to have some components using one type of DataStore and some of another type. And the Factory would be "coupled" with references to all possible DataStore, so it would be difficult to maintain.
What if the code that instantiates MyService is responsible to give it an instance of DataStore? You'll now have this:
public MyService(DataStore store) { //... }
That didn't really solved the problem... It just moved it elsewhere, but at least somewhere better. "Container" libraries, such as PicoContainer will manage the component dependencies for you by doing what is called "dependency injection":
MutablePicoContainer pico = new DefaultPicoContainer(); pico.registerComponentImplementation(MyService.class); pico.registerComponentImplementation(SQLDataStore.class); MyService myService = (MyService) pico.getComponentInstance(MyService.class);
For you PicoContainer will create a unique instance of SQLDataStore and pass it to the constructor of MyService. It more complex cases, it builds up the dependency graph for you and figures out in what order to create the components. As you can see, MyService doesn't have to rely on a static function anymore.
Other libraries such as NanoContainer and Spring offer more advanced features, for example you could define in an XML document what is the default implementation of DataStore and for some specific cases override it. Also, since the container library is the one that instantiate the class, it can do "magical" things, like not giving you the "real" instance, but instead a "proxy" that transparently adds feature to the component... I'll talk more about these later...
References:
Published on September 12, 2005 at 17:40 EDT
Older post: Making Of
Newer post: Automating Java Builds with CruiseControl