Summary
Once the boilerplate traditionally associated with the Service Locator Pattern is eliminated, one of the key motivations for Dependency Injection is reduced, if not eliminated. This article, the second of a two part series, looks at the pros and cons of each approach to interacting with services.
Advertisement
In Part
1, we looked at Bordello, a simple implementation of the Service
Locator Pattern to aid in making code more testable. Of course, the
Service Locator Pattern isn't the only way to accomplish this; using a
Dependency Injection framework such
as Spring
or Guice is
another popular technique. One reason for the popularity of
Dependency Injection is the perceived difficulty associated with the
Service Locator Pattern. Indeed, Crazy Bob Lee
touts
this as one of the motivations for Guice:
Before Guice, I had to make the same up-front decision over and
over: do I call a constructor directly, or do I write a factory and
call it instead? If you start out calling a constructor and later
decide that you need a factory, you have to go back and change all the
callers. On the other hand, factories come at a cost: they clutter
your API and result in a bunch of boilerplate code which you have to
both write and maintain.
As we saw in Part 1, it's actually quite easy to have a lightweight
Service Locator which doesn't require writing new factories for each
service. To quickly review, client code simply
calls Bordello.get to acquire services as it needs them.
public class HelloClient { public void greetFormally(String surname) {
Bordello.get(HelloService.class).sayHello("Sir " + surname);
}
}
By contrast, a client using Guice will have services injected at
construction time, and might look like:
public class HelloClient {
@Inject private HelloService service;
On the surface, DI frameworks and this sort of "generic" service
locator seem very different. It turns out, however, that while the
two approaches feel quite different, their behavior in most ways is
strikingly similar. Both use interfaces to enable testability. Both
require some means of specifying the preferred implementation of a
service; Bordello uses annotations for this (as does Guice, and later
versions of Spring), but XML or other code-based configuration could
just as easily be used, as could a convention-over-configuration
approach. To determine what DI services a class uses, you look for
member variables which are flagged for injection; to determine the
Bordello services used, look for occurrences
of Bordello.get.
There, however are a few notable differences. Perhaps the most
obvious is the ways in which each approach ties code to the framework.
On the plus side for DI, a typical DI client might not have any
reference to the framework which will be used to inject its
dependencies. As such, client code has no dependency on the DI
framework. In contrast, a client relying on Bordello will have a
reference to the Bordello class. In practice, I'm not convinced that
this is that great of a cost; I've not heard any cases of a project
switching DI frameworks in midstream. However, while a class can
reference a client which uses Bordello and be none the wiser,
typically to use a client which relies on DI, one must have the DI
framework provide that client. While this may not create dependencies
in a formal sense, it does place certain restrictions on how one can
go about acquiring instances.
Another impact of using DI is the requirement that a member
variable be declared for each injected service; depending on one's
style preferences, a setter or constructor argument might be required
as well. While this is a fairly lightweight requirement as
boilerplate goes, it does have a couple undesirable side effects. The
first effect is that (in the case of using setter methods or
constructor arguments) the services a class chooses to interact with
now become part of its API. The fact that a class is a client of
HelloService is an implementation detail, and should not
need to be advertised. The second effect is that all of a class's
dependencies must be instantiated when an instance of that class is
first created. Because of the transitive nature of DI, this
effectively means creating a whole object graph upon startup. On the
up side, this means that a DI framework can catch certain problems
with circular or undefined dependencies early rather than late. On
the down side, if you want to lazily construct a service on an
as-needed basis, you are forced to go back to the Factory Pattern, and
just inject a factory for your service, rather than the service
itself. Notably, if that service has its own requirements, the
factory may need to explicitely reference the DI framework in order
to inject dependencies.
A nice feature of Bordello is that it is self-managing; no start up
code is required. While DI frameworks generally require some initial
bootstrap work to create a container, Bordello is designed to create
service instances upon first request, with no prior initialization
work. This is particularly handy for libraries which want
to avoid creating any unnecessary usage requirements for their
clients.
One thing that Bordello does not attempt to do is provide multiple
different implementations of a service in production. This could
certainly be done; for example, an @Implementors
annotation could contain multiple @Implementation
annotations, each distinguished by some sort of key. At my company,
however, we have simply found very little need for this. Notably, we
do not produce shrink-wrapped software which would need to be deployed
in a variety of environments, so we don't need to, for example,
support multiple different ORM strategies.
An area easily addressed by both approaches is configuration of
services. For example, one can have Spring configure a service by
providing configuration information in their beans.xml file, which
Spring will then inject into their instances. With Bordello,
configuration is up to the instance itself; it helps therefore to have
a ConfigurationService which services can use; this
service of course can be acquired through Bordello.
Conclusion
Despite the cosmetic differences between the Service Locator
Pattern and Dependency Injection, the two are functionally quite
similar. The biggest difference between them is not what can be
accomplished, or even accomplished easily, but rather the style of
programming that each requires. The Service Locator Pattern is a
code-focused approach; when an instance of a service is needed, code
explicitely calls out to a service locator to obtain it. In contrast,
Dependency Injection is a more extralinguistic approach, providing
clients their dependencies without any explicit action on the clients
behalf.
Very good post, good with a little contrast to the much hyped DI.
The reason I prefer the (classic) service provider approach is that it's less black magic and more KISS. There's minimal required configuration and it's a well established practice (/META-INF...) used many places in the JDK.
In short, let the class path do the "injection". That's easy to control with an Ant or Maven script and many tools will even pick up on these (NetBeans i.e. has a "services" navigator).
I only wish it had been utilized some more. It still bugs me for instance that there is no mechanism that would allow me to write a Swing component and have it register such that it would be notified if the JVM's Locale changes, or the L&F.
"As such, client code has no dependency on the DI framework. In contrast, a client relying on Bordello will have a reference to the Bordello class. In practice, I'm not convinced that this is that great of a cost; I've not heard any cases of a project switching DI frameworks in midstream."
Staring of, nice article. I think you hit it on the nail with the above. Changing frameworks, whether DI or others happens rarely in an organization.
However that said, when using DI (either by constructor or method injection), the POJO's remain what they are, simply POJO's. DI containers tend to foster the practice of "You don't have to use me if you don't want to."
For example, with spring, if one desires to use the HibernateTemplate or JMSTemplate of the framework, one does not need to depend on the Spring Container for DI but instead simply be able to use the "bean" in question as any one would any other java object. I do understand that if one starts using specifics of the container such as autowiring, magical annotations that provide transactional features etc, then the dependency on the container becomes tighter. Pre-Spring 2.0, the tie in's with the container would have been minimal, with 2.5 an the emergence of Spring annotations, the tie in becomes tighter. I guess the same argument would apply to a SLP framework as well if it were to enforce SLP container dependencies.
However, the same is a concious choice to tie one self to a DI container, pretty much like choosing to use ORACLE specific functions when using JDBC and thus locking to Oracle as a database provider. There IMO, your statement regarding switching provider's midstream reflects true.
If one designs their solution such that it does not get into container specifics, then their POJO will be, how shall I put it a "pure pojo", i.e., re-usable regardless of the presence of a injection container or not. The dependency injection can be provided programatically to pretty much the same effect and allow for re-use of the POJO in almost any context, DI or SLP.
When I first read Robert Martin's article on the Dependency Inversion Principle, in the late 90s, I thought it was insightful and original. Then when the first rash of Java "dependency inversion" containers were released (picocontainer et al) I tried them out and quickly concluded that they were fixing a non-problem.
When Spring was released, it seemed to be in response to the excess complexity of J2EE versions 1 and 2 an EJBs. It was simpler, less ugly, but shared the approach of extracting complexity from code and moving it to configuration.
I was unconvinced and the hype didn't seem pragmatic. Less ugly is not a compelling motive for architectural change.
Martin Fowler's paper made the obvious point that ServiceLocater pattern is explicit, DI is "magic."
Ultimately it is sizzle and not substance that sells, which is why many have been convinced by the buzz around DI containers, even if they add risk and increase development costs compared to a dumber, old-school, explicit approach. Software development history is full of fads. Some of them do bring real benefit, most don't, and the fun is in choosing which ones to tie yourself to.
I suspect that part of the drive for DI in Java isn't that it solves the most pressing problems, but that it is intellectually fun. I'd rather skip that fun, which is why most of my programming today is in Ruby, where DI is an irrelevance. Just as in Java, there is hype, and magic under the covers (e.g. Ruby on Rails). There is also a counterbalancing drive to pragmatic, explicit choices (Merb, Mongrel, ...)
Doesn't this implemenation of Bordello assume you've got (one and only one) Implementation Service type (Class) and instance for all Clients that need a class of a specific interface (since we are looking it up by the interface)?
Here's an (arbitrary) example of stuff we frequently do:
public interface PatternFilter { public boolean isAccepted (String pattern); }
public class JumpOffBridgeRouter { private PatternFilter filter;
public JumpOffBridgeRouter (PatternFilter filter) { this.filter = filter; }
public void doSomething(String someInput) { //...someCodeHere if (this.filter.isAccepted (someInput)) { //jump off a bridge } else { //log a message } } }
public class FlyKiteRouter { private PatternFilter filter;
public FlyKiteRouter (PatternFilter filter) { this.filter = filter; }
public void doSomething(String someInput) { //...someCodeHere if (this.filter.isAccepted (someInput)) { //fly a kite } else { //log a message } } }
FlyKiteRouter and JumpBridgeRouter use individual instances of Filters to perform some logic (or not). By looking them up in the Bordello, we could only have one instance of a Filter since we'd resolve it by Bordello.get(Filter.class)
Eric - not sure how DIP helps in that case either.
But this does bring up a problem we've faced. (We don't use Bordello, but we use a Service Locator Pattern thyat is configured via Spring). Bordello, and, to my knowledge, DIP, does assume one impl. We have a use-case where it's the same exact database, driver, etc. but on a different server. We ended up creating a DynamicServiceProvider which reads all the applicationContext.xml, but then changes the URL.
In effect, we have something like:
Bordello.get(IFoo) and Bordello("New Orleans").get(IFoo);
> Eric - not sure how DIP helps in that case either. > in Spring (since the everything is configured from the outside)... i.e. Both FlyKiteRouter and JumpBridgeRouter are wired to have thier own the Filter instance injected as a dependency(DI)...
> But this does bring up a problem we've faced. (We don't > use Bordello, but we use a Service Locator Pattern thyat > is configured via Spring).
Actually, if you are using both ServiceLocator and Spring.... I'd wonder why... you can get rid of ServiceLocator entirely if you use Spring (but maybe Im missing something about your shop or app here)
> Bordello, and, to my > knowledge, DIP, does assume one impl. We have a use-case > where it's the same exact database, driver, etc. but on > a different server. We ended up creating a > DynamicServiceProvider which reads all the > applicationContext.xml, but then changes the URL. > Hmm.. Again I think you are talking about using both ServiceLocator and Spring DI... this issue goes away if you arent using a ServiceLocator... (Lots of shops I've been to like spring but still feel the need to create a serviceLocator cause they dont buy into what spring gives them 100%...
In spring I can create as many beans that implement the same interface as I want (i.e. I can create 100 instances of disparate classes that all implement PatternFilter)... then wire up all of my clients (in my case the JumpOffABridgeRouter and FlyAKiteRouter) using DI... easy.
> In effect, we have something like: > > Bordello.get(IFoo) > and > Bordello("New Orleans").get(IFoo); > > (obviosuly, the 2nd line isn't legal Java...)
Seems like a "leaky API" to me... Now you got the Bordello and your client code have to agree on a magic string "New Orleans" ... even if you did this: public class ThisClient {
public void doSomething() { Bordello.forService(ThisClient.class).get(IFoo);
}
...It'd be leaky (but less so) since you got a dependency from your code to Bordello, and from the Bordello Binding back to your code... All this goes away if you really relinquish control and let Spring DI do it's job.
I'm trying to understand what Bordello buys us... rather than just pass the Spring ApplicationContext around and typecast beans that you get out of it.... (Although that's not the ideal or recommended way of doing things) Also you can do ApplicationContext.getBeansOfType(MyInterface.class); so it seems like Bordello gives you less value for about the same amount of work.
> in Spring (since the everything is configured from the > outside)... i.e. Both FlyKiteRouter and > JumpBridgeRouter are wired to have thier own > the Filter instance injected as a dependency(DI)...
o.k. But then the client code needs to know which to use, which sortof defeats DI, which is "somebody else tells me what to use".
> Actually, if you are using both ServiceLocator and > Spring.... I'd wonder why...
It's an interesting compromise. Most of the classes are not DIPed, so you know what is going on and don't need setters for fields that should be final. (my main beef with DIP) The demarcation between "I know what I've got" and "I'll ask this guy where magic stuff happens" is clear.
> In spring I can create as many beans that implement the > same interface as I want (i.e. I can create 100 instances > of disparate classes that all implement PatternFilter)... > then wire up all of my clients (in my case the > e JumpOffABridgeRouter and FlyAKiteRouter) using DI...
In our case, the user has typed the URL of some computer somewhere. One that we may never of heard of before. We can't wire it up ahead of time.
> > In effect, we have something like: ... > > Bordello("New Orleans").get(IFoo); ... > > Seems like a "leaky API" to me... Now you got the Bordello > and your client code have to agree on a magic string
I should have explained better - it's actually a URL, so it's "less" magic.
I think things are getting more clear on my end...
>o.k. But then the client code needs to know which to use, >which sortof defeats DI, which is "somebody else tells me >what to use".... It's an interesting compromise. Most >of the classes are not DIPed, so you know what is >going on and don't need setters for fields that should be >final. (my main beef with DIP) The demarcation between "I >know what I've got" and "I'll ask this guy where magic >stuff happens" is clear.
You've got Spring really acting as a "Beefed up ServiceLocator" and not really as a "Container"... I'm not going to really defend this one... Alternatively (the way I think Spring is more commonly used) you allow Spring to be the container and relinquish control for the configuration of the application to Spring (ALL of the clients are "known" and wired through Spring (i.e. their Dependencies are injected through configuration in Xml or otherwise).
>In our case, the user has typed the URL of some computer somewhere. One that we may never of heard of before. We can't wire it up ahead of time.
Someone needs to map between what the user is requesting (i.e. the "url" and Interface) and a service that implements an interface (someones gotta do the negotiation)... in either Spring or Bordello youd have to create custom mapping logic or use a "Factory" or "Strategy"... Don't see how Bordello replaces Spring here (6 in one hand, half dozen in other) In Bordello, either: Bordello.get(MyServiceLookupStrategy).getServiceFor("url").doService(s erviceInput); - or - Bordello.get(MyServiceLookupStrategy, "url").doService(serviceInput); ...but then you'd have to write custom mapping logic in the Bordello framework ... not ideal IMHO
In Spring: public class Client { //Injected Dependency from Spring private MyServiceLookupStrategy lookup;
I guess what I meant by "leaky" is that the Client has to have some intelligence about the lookup of a service... (the Service API is leaking out to the client)... In your situation, the client definately knows about the target service (they passed in that information as a parameter)...
> Doesn't this implemention of Bordello assume you've got > (one and only one) Implementation Service type (Class) > and instance for all Clients that need a class of a > specific interface (since we are looking it up by the > interface)?
It does. However, bear in mind that our implementation has been based on our needs. We haven't had a need, as you have, for a large number of different implementations of a single interface.
I'm curious - when you do this in Spring, do you use XML or annotations? If annotations, do manage to avoid using "magic strings" in your wiring?
If I needed to do something like this in Bordello, I'd be inclined to add the following methods:
publicclass JumpOffBridgeRouter {
publicvoid doSomething(String someInput) {
if (Bordello.get(PatternFilter.class, JumpOffBridgeFilter.class)
.isAccepted(someInput)) {
// jump off a bridge
}
else {
// log message
}
}
}
Client test code might call:
Bordello.set(
PatternFilter.class,
JumpOffBridgeFilter.class,
new MockPatternFilter(true));
new JumpOffBridgeRouter().doSomething("test");
assertThat(Bridge.jumpedOffOf());
Of course, this does place an additional responsibility on the client to know what it's filter's implementation should be; whether or not that's a bad thin depends on the structure of the application.
> I'm trying to understand what Bordello buys us... rather > than just pass the Spring ApplicationContext around and > typecast beans that you get out of it
Other than taking advantage of generics to avoid a cast, it buys you an architectural style which favors more explicit control over external configuration by a container. If that's something you value, then it makes sense to use; if you get value out of the container approach, you should probably use that. I don't consider either style to be inherently superior to the other. I do find it interesting, though, how much of our industry has lately come to assume that many problems can only be solved with DI.
Thanks for the response, to answer your question, I generally use Xml... sometimes I've tinkered with programmatically creating applicationContexts (for tests)... it's quite nice for the quick and dirty.
FWIW I never use annotations, and I agree with your sentiment. By the way, my bad on reiterating in my post what you had spelled out in the article... (I guess it's more of a concern for me in my current environment).
I realize that you are speaking of a particular instance where Bordello helps you, that's fine... In certain situations I've stripped out Spring Wiring in leu of something more programmatic (this happened when I had to extract a small service from a large application and deploy it as a separate WAR)... for a small app that made sense (no need adding all the overhead of Spring for a single dao).
My concern was that we were putting Bordello on the same level as a full fledged DI container and talking about it's strengths and weaknesses. As previously stated, if you are doing something that works for you (in the specific) I wouldn't change it, but in the general sense, I would argue that (for a heavy duty app) there is so much going on that trying to "roll your own" is likely not such a great idea.
Specifically, in the example you gave...(Maybe I should have been more clear in my explanation)... Lets assume a any PatternFilter class has one roll, it takes a String and returns true or false if the String matches a pattern or target... There may be many implementations of this PatternFilter Class (RegexPatternFilter, InSetPatternFilter, StartsWithPatternFilter, EqualsIgnoreCasePatternFilter)
The PatternFilters themselves don't know anything about routing, they are a generalized framework/utility class.
Hence in your example I'd argue that we'd need to create a new Class (i.e. JumpOffABridgeFilter) for each person who would like to use it (in Bordello). (How would you manage something like multiple Database Connection Pools?... would you need to create a new Subclass the connection Pool for each individual Client?)
ABCService service = (ABCService) ABCServiceFactory .singleton().createService( anGetXyzArgs.getXyzObjectId(), getLocale(aSession));
Like the above , and some ejb code is also dependent upon the same, can i advise here to switch to spring 's DI and just create two configuration files one for the DAO part and another for the Services part and take out all these boilerplates . 1>How much effect and cost will this have and is this advisable.