Hacking on GraphHopper - a Java road routing engine. Peter has posted 62 posts at DZone. You can read more from them at their website. View Full User Profile

Dependency Injection - 3 Questions!

06.27.2008
| 7340 views |
  • submit to reddit

Normally the reason why I write blogs is to inform other people about interesting things I found or I am thinking about. Today I will ask some questions and wait for your answer ;-)

One of my latest posts was about the spring rich client. Because of this project and picocontainer I read a lot about design patterns. The most obvious design pattern in spring is called dependency injection. This design pattern helps the software architect to loosly couple components. It could work like follows (just for the JSE-developers under us):

class ClassA {
MyInterface objectA;
public void setMyInterface(MyInterface objA) {objectA = objA;}
}

where we could have defined the interface like this:

interface MyInterface{ String toString(); }

Now, ClassA defines a dependency on an instance of MyInterface and the framework (for Java e.g. picocontainer, spring, juice or others) will inject an instance of an implementation of MyInterface into ClassA. It is up to the configuration if objA is a newly created instance or if it is a ’singleton’. (MyInterface could be a class as well.)

For example if you call

ClassA classA = framework.getBean(ClassA.class); //code is similar to picocontainer; not to spring!

You will get back an instance of class ClassA, where objectA is not null! The dependency was defined by the method (setMyInterface) - this is called setter injection. Other kinds of injections are:

  • annotation-based injection:

    class ClassA { @Inject Object objectA; } 
    

  • constructor injection:
    class ClassA { Object objectA;
    public ClassA(ObjectA objA) {objectA = objA;}
    }

Where picocontainer campaigns for constructor and spring for setter injection (but both projects offer at least the mentioned kinds of injection).

It is correct that you can create your own small framework or even set up the objecs by hand to achieve the same: dependency injection. But I guess you are lazy and will choose one of the open source frameworks.

Now, all is explained to understand my 3 questions. Hopefully someone out there will have an answer!

1. How should I design a library?

Let me explain. You want to sell a nice designed, but complex library. The library offers a lot of functionality and you want to split it into loosly coupled components. That means you have to use dependency injection (DI), but you don’t know if the customer has or wants such a DI-framework.

So, if you use e.g. the annotation-based injection it would be nearly impossible for the customer to use your library; with setter or constructor injection it is quite difficult to set up the objects for using your library.

I don’t know what to do: Should I really use dependency injection here? Or should I use a (dynamic) service locator instead; to decouple my components within the library?

Martin Fowler says:

“It (dependency injection) tends to be hard to understand and leads to problems when you are trying to debug. [...] This isn’t to say it’s a bad thing, just that I think it needs to justify itself over the more straightforward alternative (service locator).”

A common reason people give for preferring dependency injection is that it makes testing easier. The point here is that to do testing, you need to easily replace real service implementations with stubs or mocks. However there is really no difference here between dependency injection and service locator: both are very amenable to stubbing. I suspect this observation comes from projects where people don’t make the effort to ensure that their service locator can be easily substituted.“

“So the primary issue is for people who are writing code that expects to be used in applications outside of the control of the writer. In these cases even a minimal assumption about a Service Locator is a problem.”

So: What would you do? Would you use a service locator or dependency injection inside the library?

And this leads me to the next important question:

2. Why is a singleton an antipattern?

I stumbled over this question while I used picocontainer. They say you have to avoid singletons, because it is difficult to test and to replace. My question is: why? Imagine the following singleton:

class LogFactory{
public static Logger getLogger(Class clazz) { … }
}

Now it is really easy to replace the implementation of the Logger interface e.g. if one defines a setImpl method:

LogFactory.setImpl(LoggerForTesting.class);

So what’s soo wrong with a singleton?

3. How can I avoid making all classes public?

I don’t know if it is the case for autowiring in spring. But in picocontainer you have to set-up the configuration for the wiring of the classes by yourself e.g. in a separate class (with Java; no xml! compiler checks references! yeah!) or in a separate file with a scripting language (or xml) and nanocontainer. That means you could specify the implementations of the interface by:

framework.setImpl(MyInterface.class, FirstImpl.class);

But wouldn’t it mean that I have to add the modifier public to the class FirstImpl? (To let picocontainer call the default constructor.) This can’t be good design to allow the user of my library to see the implementations of MyInterface.

Whats wrong with my use case and my conclusion?


From http://karussell.wordpress.com

Published at DZone with permission of its author, Peter Karussell.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Brian Sayatovic replied on Fri, 2008/06/27 - 9:02am

Singletons are a pattern worth avoiding.  For one, it is very hard to guarantee a singleton.  A singleton means there can be only one.  It is very hard to enforce that.

 Most of the time people don't use singleton because they want there to be only one instance.  They do it because they want a globally accessible instance, often lazly loaded.  That's different than singleton.

Also, globals and singletons make testing harder because they're scoped to a single runtime, but testing frameworks often run all of your tests in single runtime.  This means that the singleton/global becomes an avenue for unintended side effects between otherwise independent tests.

Brian Sayatovic replied on Fri, 2008/06/27 - 9:08am

Your point #3 is a good one.  Too many frameworks have requirements that your classes be public, have public no-arg constructors and public setters.  If an object is to encapsulate and guard its state, I prefer to make it such that my objects can't be instantiated in an invalid state (when "new" is done, the object and its state is all valid).

Ronald Miura replied on Fri, 2008/06/27 - 9:09am

Singleton isn't an anti-pattern per se. People tend to say that because it is so misused that (they say) it would be better if you never use it. Singleton is 'global variables reborn', and everybody 'knows' globals are evil. :)

Dependency Injection is a useful pattern, and like all patterns, it can be misused. But it does have many advantages over Service Locator.

First, a class that requires a dependency will always depend on the Service Locator class, and can't be used in isolation (it may or may not be an issue, but it is less 'loosely coupled' than the DI equivalent).

Also, the Service Locator requires the calling class to have enough 'information' about the dependency to make the lookup. For example, if you just ask for an object of type 'T', and your Service Locator may contain references of two implementations (or two different instances, say, two Hibernate SessionFactory instances mapping two different DBs), how do you resolve this? One solution is to pass a String with a unique-identifier for the dependency, so, you can't say that the calling object 'doesn't care' about what is returned from the lookup, it is explicitly choosing one dependency from many. Other solution is configuring the Service Locator to return a specific instance when called from class A, and another from class B (could it be named 'Locator-based Injection'? :D).

'Classic' annotation-based wiring (Guice's @Inject, Spring's @Autowired/@Qualifier, EJB3's @EJB, Seam's @In) is a kind of Service Locator. You hard-code an identifier (bean name, JNDI name, whatever) directly into the calling class, instead of specifying it in external configuration. You don't have a method call to a Service Locator, but the effect is the same. This is the reason Guice is quite nice for application wiring (the classes you'll configure are your own, and you can modify them when the dependencies change), but not very suitable (IMHO) for use inside a framework, where application developers can't change it if needed (to resolve ambiguities, for example).

That said, DI does have its shortcomings. Configuration files/classes tend to become very verbose if you decide to configure everything by DI. If you want tight control of the scopes of dependencies from within a calling class (get a new instance or reuse as needed), a Service Locator (or Factory, or 'Provider' as Guice calls it) may be a better fit. For application wiring, when you have only one implementation for most components (say, Controllers, DAOs, Façades, etc), Service Locators or annotation-based wiring are usually much less verbose than external wiring, either XML or java (even so, external wiring have its advantages, too, for example, it's easier to create a 'blueprint' of the whole application from XML metadata).

In the end, it's all about common sense and conscious design. And the old, catchy phrases, such as 'there is no silver bullet', 'there is no one-size-fits-all', etc. are still valid anyway.

*** I use the term 'classic' annotation-based wiring because Spring-JavaConfig uses a completely different approach for annotation-based wiring, where you create an external annotated configuration class, instead of requiring calling classes to be annotated.

Steven Bazyl replied on Fri, 2008/06/27 - 11:30am

The mistake here is in thinking that DI is mutually exclusive with other patterns.  Just because you use it for one thing doesn't mean you have to use for all things. 

For example, there isn't much value in injecting trace logs...it's just not something you're going to need to change at any point, and you'd just be adding complexity by making it injectable.  Likewise, you don't necessarily want to inject *every* dependency, particularly not package-local helper classes or other things that are unlikely to change subject to configuration.  If you limit DI to coarse-grained dependencies (between services or layers) you'll find it much easier to get your head around.

Regarding libraries, there are quite a few libraries out there that have managed to accomodate both users with a container like spring and those without.  If you give people the parts to wire up they can easily write the code themselves, or if you want to make it easier for them, create some simple bootstrap classes to do it for them.  Even within the spring itself you'll frequently find cases where components are initialized with reasonable defaults and dependencies.  That is, you *can* inject other dependencies if you need to, but things should self-configure as best as possible.

 

 

 

Jakob Jenkov replied on Sat, 2008/06/28 - 1:02am

I have written a small text as notes to myself on when and when not to use dependency injection. You can find it here:

when to use dependency injection

 

1) Regarding API-design:

 - Only make the parts public that the user needs to interact with. 

 - Only make injectable what the user will actually need to change (config or special implementations).

 - The rest you make non-changable and non-public.

For instance, I'm currently reworking MrPersister into Butterfly Persistence (Persistence API /ORM) , and cleaning it up while doing so. After the redesign the user needs to instantiate only one class: PersistenceManager. The only thing that can be injected into the PersistenceManager at this time is a DataSource. Many classes are instantiated or accessed internally in the PersistenceManager, but the user cannot inject those. Only what the user actually needs to inject is injectable. The rest is just wired up with non-public implementations.

 

2) Regarding  Singletons:

Static singletons (those guarded by static methods) are evil. I actually wanted to write a text on that a few years ago,  but never got around to doing so... maybe I should :-)

Normally you'll only have a static getInstance() method and not setInstance(), to make sure the instance is a singleton. In many cases you will write your class to call this method (getInstance()) rather than have the singleton injected. This makes it hard to change the singleton implementation during testing, which is often necessary. Here is what I mean:

 

//direct access = hard dependency on singleton getInstance() method.
public myMethod(){
Singleton.getInstance().doSomething();
}

 

Instead you could do this, using an injection style:

public class MyClass{

protected Singleton singleton = null;

public MyClass(Singleton singleton){
this.singleton = singleton;
}

public void myMethod(){
this.singleton.doSomething();
}

}


//instantiating MyClass:

MyClass myClass = new MyClass(Singleton.getInstance());

 

Now the myMethod() no longer depends on the Singleton.getInstance() method, but  only on a valid Singleton instance.

If you add a setInstance() method to the Singleton class, how do you make sure it is only called during testing when it is allowed, and not during production runtime?

Peter Karussell replied on Sat, 2008/06/28 - 6:55am

@ Jakob:
>> Only what the user actually needs to inject is injectable.
How would you know this? But maybe it is true what Alex M. (comment on orig. blogentry) says: These days I generally care a lot less about public/package/private.

See the original blogentry for additional ideas.

Here a short summary:

from Alex Miller's article:

1. A Singleton hides dependencies (like SL do as well)
3. Hard to subclass
(But couldn't one make it replaceable with setSingleton?)
4. It’s a lie: Singletons in Java are based on static variables, which are held per-classloader, not per-VM.
5. A singleton today is a multiple tomorrow - It’s not at all unusual to discover that you now need 2 or more of something you previously only needed one of.

from fletch: Instead of using a ServiceLocator (which will tie users of your library to your ServiceLocator class), try and create 3 or 4 large composite objects that are preconfigured in what you’d consider a standard sense.

My notes:
1. While reading all your useful comments I got another point:
And one big drawback of a singleton is threading! Either the singleton is not thread-save or you can get performance problems, because of synchronization.

2. avoid singletons
3. do not configure everything with DI, but prefer this pattern. from Ronald: ‘there is no one-size-fits-all’
4. maybe Java should remove the keyword ’static’ from its language specification ;-)

Jakob Jenkov replied on Sat, 2008/06/28 - 8:49am

Good points. I recognize several of the points I wanted to make in that text. An a few new ones :-)

Static methods can still be useful for basic utility methods, so I don't think the keyword should be removed completely· Just don't use it to enforce singletons. Use a singleton-by-convention instead, meaning it's a singleton because you ever only instantiate one. This can be enforced by a DI container.

Robin Bygrave replied on Sun, 2008/06/29 - 4:35am

Interesting questions and discussion... here's my 2c...

1.  How should I design a library?

IMO I think you would be very bold to build a library dependant on DI, but obviously it depends on the requirements of your library.

You may want to use DI rather a simple 'pluggable implementation/factory' approach when the pluggable implementation itself is dependant on other pluggable implementations (dependancy dependant on another dependancy dependant on another ...). This is where the simple approach could get ugly compared with DI.

You also may have the need for scopes that DI containers typically provide (session, conversation)? 

You may also have an AOP requirement that DI containers also typically provide?

Being a Library (as opposed to an app) I would suspect the simple approach will suffice and that your library will not require a DI container (or special scopes or AOP support).

 

2.  Why is a singleton an antipattern?

This depends on the 'definition' of singleton... in that often factory patterns are used with singleton and so it can get a bit blurred with other patterns (Static Factory, Registry, Service Locator patterns) - especially when you are talking about it in the context of DI. For me the issue/argument with singletons comes down to two things but firstly....

In regards threading/synchronization, a singleton implies a shared resource so it is implied that the code should be written in a threadsafe manor. In my book this is not a valid argument against singletons.

Also, if developers are writing their own ClassLoaders then they should be comfortable with a singleton dependant on a ClassLoader. IMO this is actually what you want rather than the other way around (separate context for each webapp/module etc) so as long as this is understood this is fine and I'd argue more a benefit than a problem.

 2.1 Singleton Issue 1 - hard to change the implementation

This issue comes down to the way you write the code. Specifically I tend to think of the Singleton itself as final class (not really suitable for subclassing) so whatever code you put in the singleton itself is not easily replaced. This could loosely be extended to anytime you have a static method (getInstance() in this case). So if you put implementation code into the Singleton itself then you are stuck with it  (so don't put implementation code in the singleton).

If you just use the singleton as a way to provide static access to a 'pluggable' implementation then for me this is fine. It could be argued that this is more a 'static factory' or 'service locator' rather than a 'singleton'. Another way of thinking about this is that I'd suggest you generally want to separate the SCOPE from the API and in general you don't want any API implementation in the singleton itself.

2.2 Hard to change the scope (from Singleton to something else)

Generally the point here is that a Singleton by design is trying to scope itself to be a single shared instance. For a application wide shared resource this is what you want but you have to be sure that this scope makes sense for the future.

 

Do you have an actual code example from your library to discuss?

Wilco Boumans replied on Mon, 2008/06/30 - 2:40am

What I find missing in most DI frameworks is 'modularisation'. Having worked with ATG for some years, I really appreciate this mechanism in ATG. The basic concept:

 An ATG module consists of java classes and property files. The property files fulfill the role of XML-based dependency injection. A module could be for instance provide shopping cart, including discount engine, price calculators, ...  Modules can be stacked, which means they inherit each-others classes AND property file. A property file defines the values an instantiated object has, the references to other objects it receives and it's class and scope (request, session, global). The nice thing about the construction is that you can override the values of an object in your own module. If you need to extend the class, replace it with your own. If you need to change object values, do it in your own module.

Basically, the provided network of objects the library (module) provided can be modified with customisations by 'inheriting and extending' the dependency injection definition that comes with the library.

 

Peter Karussell replied on Mon, 2008/06/30 - 8:58am

>> In regards threading/synchronization, a singleton implies a shared resource so it is implied that the code should be written in a threadsafe manor. In my book this is not a valid argument against singletons.

Why not??

>> Do you have an actual code example from your library to discuss?

Not really, I have to think about it...

But one (not soo good) example: how would you design a system, which searches for a car request from a customer through some renter companies?

Imagine you have one implementation LayerImpl of the main interface 'Layer'.
How would you force the user to create it? Via LayerFactory.createLayer or simply via new? (I guess DI is some overkill at this point)

Now you can get all prices via:

Map<Price, Car> prices = layer.getPriceForCars(gps, carRequirementsOrProperties);

But all implementations (say Avis, Europcar, ...) of the interface Renter has to know some global properties (how many responses they should wait for, etc.).
How would you tell the implementations of Renter the properties?

You could use a Layer.getSingletonInstanceProperties() method, but ah(!), okay, maybe I got it:
just inject the property object into the renter objects 'by hand':

class Layer {

List<Renter> getAllRenter() {

renterList.add(new AvisRenter(properties));

renterList.add(new EuropcarRenter(properties));

renterList.add(new TuttiFruttiRenter(properties));

return renterList;

} } 

Yves Bossel replied on Mon, 2008/06/30 - 10:20am


I came through the same problem while maintaining a banking system. Where introducing a whole framework was forbidden, however using DI made design clearer, testable, maintainable, ...

This description is based on my experience with constructor injection using PicoContainer. I have no experience with other DI containers.

Back to the basics: which dependencies?
The point about constructor injection is following: whenever you instanciate an object you are defining the "plumbing" of your code. More specifically, when instantiating an object, you define which objects are reachable from the instantiated one. The instantiation also defines the paths through which you can reach the other objects: these paths are the variables that hold references to your objects. Of course, you can reassign a variable an change the "plumbing"; even so, you have to instantiate the object and hold a reference to that object.
Thus the declaration SomeObj value = new SomeObjImpl(...) is hard-coded coupling.

Using constructor DI, you make those dependencies visible. You also define a point of code where the dependencies are resolved: the constructor. Therefore the constructor is the point of definition your whole code's "plumbing".
That way, by moving all "new" from your code into a single location, the DI container, you get an orthogonal dimension of your code: its "plumbing".

The "plumbing" is isolated into the DI container. Which is good, because now you can look at the coupling and you can restructure your code and adapt its behavior from a single point.

Problem: where to obtain the actual objects?
Now you have to obtain a reference to the objects you will use. And at that point, you establish coupling, maybe lighter than a "new" if you use a Service Locator or a factory, but still coupling.

In any framework, its entry points are stable, per definition. So, in order to minimize the coupling, the solution is to obtain the actual implementation of the objects at the "entry points": this is the "main" of the program, the service() method of a servlet, the beforeAction() in a BEA PageFlowController, etc.

Typically when defining a component (servlet, portlet, etc.), I define an inner class named MyComponent.Dependencies and register that class in the DI container. It has a private void init( MyComponent comp ) method that initializes the component, and in the component's entry point I obtain the actual implementation of its corresponding Dependencies object. That way I do not expose my component, nor its initialization process (no evil public setter, everything is private).

 

public class SomePageFlowController extends PageFlowController
{
//-----------------------------------------------------------------------
// instance variables
//-----------------------------------------------------------------------
private transient UserContext m_usr_ctxt;
private transient PasswordCheckerResult.Factory m_result_factory;


//-----------------------------------------------------------------------
// Dependencies
//-----------------------------------------------------------------------
/** Resolves the dependencies of this page flow.
*/
public static final class Dependencies
{
private UserContext m_usr_ctxt;
private PasswordCheckerResult.Factory m_result_factory;

public Dependencies( UserContext user_context,
PasswordCheckerResult.Factory result_factory )
{
m_usr_ctxt = user_context;
m_result_factory = result_factory;
}
private void init( SomePageFlowController jpf )
{
jpf.m_usr_ctxt = this.m_usr_ctxt;
jpf.m_result_factory = this.m_result_factory;
}
}//end class Dependencies


//-----------------------------------------------------------------------
// init
//-----------------------------------------------------------------------
/** Initializes this page flow's dependencies before invoquing any action.
*/
protected void beforeAction()
{
String key = Dependencies.class.getName();
Dependencies deps = (Dependencies)
ComponentLocator.Impl.getInstance( getRequest() )
.getComponentInstance( key );
deps.init( this );
}

//...
}

Problem: library design.
Libraries are more complicated, because there is not a single stable entry point.
Anyway, you first have to ask yourself for whom you want DI. This is specially valid when the user of your library does not want to use a DI framework.
Another question, is how to enable DI for those who want to use it with your library.
Last, but not the least, is how to keep your DI structure modular.

First, when designing a library you have to provide a default implementation, and probably many variants. This is where you want to use DI. There you can use DI internally, the (embedded) DI container provides the default behavior.

To do that, you define a "dependencies" package that contains a class that extends the DI container and holds the dependencies for the defaults of your library. To declare the dependencies of a class that can be instantiated, just declare a helper class MyDefaultObjDependencies that will be registered in your DI container extention. When MyDefaulObj is instantiated it invokes your DI container extension; obtains an instance of MyDefaultObjDependencies; and initializes itself, as shown in the previous example.

At that point, unless your library's client code uses DI, that client code is tightly coupled to your library through following invocation:
IMyObj value = new MyDefaultObj();

You can make things a little bit more configurable, by enabling the replacement of your default dependencies. Eg. in MyDefaultObj constructor, check for a SystemProperty "mypackage.dependencies" which specifies the name of the singleton to use to obtain the DI container that contains your dependencies. So you can change the structure. This point is important, because if your customer does use a DI framework, your code needs to access the actual container instance.

The last problem is dependencies maintainability.
At least PicoContainer lets you compose DI containers. That way you can reuse the defaults provided in the example above, keeping things modular.

Robin Bygrave replied on Wed, 2008/07/02 - 5:21pm

>>>

>> In regards threading/synchronization, a singleton implies a shared resource so it is implied that the code should be written in a threadsafe manor. In my book this is not a valid argument against singletons.

Why not??

<<<

The goal of a singleton is to provide a shared resource. You could also achieve this with Servlet Application scope or DI with singleton scope. These are 3 approaches you could use to get a shared resource (which by implication is shared across threads in a typical multi-threaded app).

No matter which approach you take the shared resource should be written in a thread safe manor. Thread safetly is a requirement for each of the three approaches, not just singleton (code pattern).

Conversely, if you don't want a shared resource don't use a singleton (or Servlet Application scope or DI with singleton scope).

 

 

Robin Bygrave replied on Wed, 2008/07/02 - 6:03pm in response to: Peter Karussell

>>> >> Do you have an actual code example from your library to discuss?

Not really, I have to think about it...

<<<

Interesting because I was thinking down a different path. When you said "Library" I was thinking something more generic like a Scheduling service or Text indexing service etc where as your Car Rental code example looks more domain specific/application/module to me.

So to continue my line of thinking (maybe this helps?)...

- Lets say I have a Scheduling Service

- It has an interface called ScheduleStore that loads/saves schedule meta data

- I might provide 2 implementations that loads/saves to an XML file and via JDBC to DB

- Anyone can write their own implementation

There is a known properties file (called schedule.properties) with a known location and it has a property in it to define which implementation is used...

# specify which implementation of ScheduleStore to use

schedulestore=org.banana.schedule.XmlScheduleStore

#schedulestore=org.banana.schedule.JdbcScheduleStore

...

To initialise the correct implementation you get the property as a String which is the className of the implementation and then use something like Class cls = Class.forName(implementationClassName); ScheduleStore store = (ScheduleStore)cls.newInstance(); // assuming a default constructor

Hopefully that made some sense. The upside of using this approach is that you could give this library to anyone without a dependancy on DI (also depends on the value you'd get from embedded DI into your library).

 

Getting back to your Renter example... I'd have to think more about it.

Wilco Boumans replied on Thu, 2008/07/03 - 1:03am

>>>

 >> Hopefully that made some sense. The upside of using this approach is that you could give this library to anyone without a dependancy on DI (also depends on the value you'd get from embedded DI into your library).

<<<

 This will work, but in fact you're introducing your own 'DI framework-independent' dependency injection. By injecting global objects instances of your own classes into the pre-packaged library, you can influence it's behaviour somewhat.

The other way to deal with this, is to extend the DI framework with the possibility of customisation. Advantages in that case are:

  • Extending/customizing the library uses one DI framework
  • Scoping can be extended to more than global
  • More of the DI framework's features can be used
  • Many commercial/opensource libraries that contain working but customizable solutions can become available. Extensions can be made without the need to have the source code

Yves Bossel replied on Thu, 2008/07/03 - 4:11pm in response to: Robin Bygrave

To initialise the correct implementation you get the property as a String which is the className [...]

Hopefully that made some sense. The upside of using this approach is that you could give this library to anyone without a dependancy on DI (also depends on the value you'd get from embedded DI into your library).

Be careful this solves one aspect but introduces other problems. First of all, let's have a look at the main aspects you have to solve:

  • Choose the technique to use to make things configurable.
  • Make your design maintainable (avoid coupling).

 

The point is not (yet) how you get the concrete implementation of ScheduleStore, but where you obtain it in your code. The invocation of the mechanism that resolves the concrete class of ScheduleStore is what couples your code to the chosen mechanism. The same way the "new" keyword is what couples your code to a concrete class.

So first choose a mechanism to resolve the concrete class of ScheduleStore and define a standard way to resolve it. E.g. a singleton, a helper object or a static methods utility class that hides the configuration and components' resolution mechanism from the clients, so that you can invoque it easily (syntactical sugar), eg.

ScheduleStore schedule = ResourceLocator.getInstance().getComponent( "org.banana.schedule.ScheduleStore
" );

At this point:

  • You have a way you like, to obtain the concrete implementation of ScheduleStore.
  • The ScheduleStore clients are protected from changes.
  • ResourceLocator uses your preferred configuration mechanism (e.g. System properties)


Now the BIG point is how design maintainable code.To do that avoid coupling.

  • Base your design on interfaces (ScheduleStore should be an interface) it makes easier to replace the concrete implementations. Inheritance works also, but is more limiting.
  • Use the ResourceLocator only at your code entry points (main(), servlet's service(), etc.) If you invoke the ResourceLocator deeper in your code, your code gets coupled to the ResouceLocator.
  • Define the concrete implementation dependencies structure using DI (better, use constructor DI).

Why have you to use DI? Because it helps you to compose the whole structure of your code instead of hard-coding it each time you instantiate an object ("new ..."), each time you obtain a singleton, and each time you invoke a static method.

Composition lets you explore the design, and replace only a few classes instead of having to change lots of deeply nested invocations that are tied up by the coupling invocations I just mentionned.

As you apply these recomendations, you will start to apply lots of design patterns. They become natural. In particular, first appear lots of factories (to avoid being coupled to the "new SomeConcreteObjectICouldChange( VerySpecificParams) ), then the other patterns make a lot of sense.

Then you start thinking about the dependencies, instead of thinking about how to pass a parameter that is needed by an object that is instantiated very deeply in your structure (a smell of high coupling).

Finally you can very easily unit-test each class, since you pass any dependency in its constructor.

Robin Bygrave replied on Thu, 2008/07/03 - 7:49pm in response to: Wilco Boumans

@Wilco

>>   This will work, but in fact you're introducing your own 'DI framework-independent' dependency injection.

That is the EXACT point. There are plenty of Libraries out there that are not dependant on Guice/Spring/PicoContainer etc for example. You can solve this problem with DI (and a DI container) or you can write your own java code that does something similar - which I'd describe as more "Pluggable Implementation" rather than "Depedancy Injection" (because as I see it there is no "injection" really with the pluggable implementation approach).

The point being there is a continuum of complexity and functionality and at the low end you may choose not to use DI. I actually made the point in an eariler post that DI also gives you more scoping and AOP support but if you don't need those features and you don't have dependancies which in turn have dependances etc (where injection becomes king) then you may find DI overkill.

Hey, I like DI but I think there are cases where a simple approach is also good.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.