The Artima Developer Community
Sponsored Link

Weblogs Forum
Service Locator Pattern Revisited, Part 2

12 replies on 1 page. Most recent reply: Nov 20, 2012 3:18 AM by bineet mohanty

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 12 replies on 1 page
Ian Robertson

Posts: 68
Nickname: ianr
Registered: Apr, 2007

Service Locator Pattern Revisited, Part 2 (View in Weblogs)
Posted: Sep 16, 2008 9:17 PM
Reply to this message Reply
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;

  public void greetFormally(String surname) {
    helloService.sayHello("Sir " + surname);
  }
}

Comparing Bordello with Dependency Injection

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.


Casper Bang

Posts: 12
Nickname: mrmorris
Registered: Nov, 2007

Re: Service Locator Pattern Revisited, Part 2 Posted: Sep 18, 2008 12:32 PM
Reply to this message Reply
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.

Sanjay Acharya

Posts: 1
Nickname: ghostdrvr
Registered: Sep, 2008

Re: Service Locator Pattern Revisited, Part 2 Posted: Sep 18, 2008 9:45 PM
Reply to this message Reply
"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.

Peter Booth

Posts: 62
Nickname: alohashirt
Registered: Aug, 2004

Re: Service Locator Pattern Revisited, Part 2 Posted: Sep 19, 2008 6:01 AM
Reply to this message Reply
It's interesting to read this article.

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, ...)

Eric DeFazio

Posts: 4
Nickname: mdef
Registered: Sep, 2008

Re: Service Locator Pattern Revisited, Part 2 Posted: Sep 22, 2008 2:44 PM
Reply to this message Reply
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)

Morgan Conrad

Posts: 307
Nickname: miata71
Registered: Mar, 2006

Re: Service Locator Pattern Revisited, Part 2 Posted: Sep 22, 2008 3:43 PM
Reply to this message Reply
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);

(obviosuly, the 2nd line isn't legal Java...)

Eric DeFazio

Posts: 4
Nickname: mdef
Registered: Sep, 2008

Re: Service Locator Pattern Revisited, Part 2 Posted: Sep 22, 2008 8:02 PM
Reply to this message Reply
> 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.

Morgan Conrad

Posts: 307
Nickname: miata71
Registered: Mar, 2006

Re: Service Locator Pattern Revisited, Part 2 Posted: Sep 23, 2008 8:21 AM
Reply to this message Reply
> 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.

Eric DeFazio

Posts: 4
Nickname: mdef
Registered: Sep, 2008

Re: Service Locator Pattern Revisited, Part 2 Posted: Sep 23, 2008 2:23 PM
Reply to this message Reply
Hey Morgan,

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;

public String runService (String url, String input) {
return lookup.getServiceFor("url").doService(input);
}
}


>it's actually a URL, so it's "less" magic.

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)...

My 2 cents.

E.

Ian Robertson

Posts: 68
Nickname: ianr
Registered: Apr, 2007

Re: Service Locator Pattern Revisited, Part 2 Posted: Sep 23, 2008 10:57 PM
Reply to this message Reply
> 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:
  public static <T> T get(
    class<T> interfaceClass,
    class<? extends T> implementationClass) { ... }
 
  public static <T> void set(
    class<T> interfaceClass, 
    class<? extends T> defaultImplementationClass, 
    class<? extends T> mockImplementationClass) { ... }

Then client code could look like:
public class JumpOffBridgeRouter {
  public void 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.

Ian Robertson

Posts: 68
Nickname: ianr
Registered: Apr, 2007

Re: Service Locator Pattern Revisited, Part 2 Posted: Sep 23, 2008 11:12 PM
Reply to this message Reply
> 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.

Eric DeFazio

Posts: 4
Nickname: mdef
Registered: Sep, 2008

Re: Service Locator Pattern Revisited, Part 2 Posted: Sep 24, 2008 8:40 AM
Reply to this message Reply
Hello Ian,

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.

(Here's a coworkers blog link to using it)
http://www.digizenstudio.com/blog/2007/01/14/programmatically-build-a-spring-application-context/

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?)

bineet mohanty

Posts: 1
Nickname: bineetk
Registered: Nov, 2012

Re: Service Locator Pattern Revisited, Part 2 Posted: Nov 20, 2012 3:18 AM
Reply to this message Reply
This is such a nice write up on the necessities.

Can you please advise if a finacial project uses

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.

regards,

Bineet

Flat View: This topic has 12 replies on 1 page
Topic: In Defense of Pattern Matching Previous Topic   Next Topic Topic: Style is Substance

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use