Paul M. Jones

Don't listen to the crowd, they say "jump."

Quicker, Easier, More Seductive: How To Tell A DI Container From A Service Locator

(In this unexpected series, we’ve been talking about the use of Service Locator versus Dependency Injection.)

UPDATE: Please see the next post in this series for important corrections and updates regarding the essay below.


It is easy to confuse a Dependency Injection container with a Service Locator. They are very similar to each other. The differences are subtle. Indeed, it’s even possible to use a Dependency Injection container as a Service Locator, although I think it’s difficult to use a Service Locator as a Dependency Injection container. They are both sub-patterns of a more generic pattern called Inversion of Contol, and some people confuse the term Dependency Injection with the more generic term Inversion of Control.

How To Tell The Difference

I’ve been thinking about what heuristic or rule-of-thumb one could use to easily tell which is which. So far I’ve been leaning in the direction of: “Can you create new instances without defining a service?” If you can, it’s a Dependency Injection container; otherwise, it’s a Service Locator. There are several variations on that prhasing:

  • If you can create a new instance of a class without having to define a service, it’s a Dependency Injection container.

  • If you have to define a service in order to get an object out of the container, it’s a Service Locator.

  • If you cannot create a new instance of a class without defining it as a service, it’s a Service Locator.

By this I mean that you should be able to repeatedly create new, discrete, independent instances of a class, without having to define it as a service in the container.

Examples

Given the above heuristic, here’s an example of Dependency Injection using Aura.Di:

// they are separate instances, not shared services
$foo_1 = $di->newInstance('FooBarBaz');
$foo_2 = $di->newInstance('FooBarBaz');
var_dump($foo_1 === $foo_2); // false

Might you need to set up constructor parameters, setters, etc. for the newInstance() calls? Sure; Aura.Di allows for inherited and overridable constructor parameters and setter methods:

// given __construct($gir)
$di->params['FooBarBaz']['gir'] = $di->newInstance('DibZimGir');

// given a setter method setOperation($operation_name)
$di->setter['FooBarBaz']['setOperation'] = 'ImpendingDoom';

The main point is that whenever the Dependency Injection container creates a class, it can do so without having to define that class as a reusable service.

In contrast, a Service Locator requires that you define a service, and then you can retrieve a shared instance of that service from the container. Here’s an example with my trivial Service Locator example from earlier in this series:

// define shared services
$locator->set('gir', function () use ($locator) {
    return new DibZimGir;
}

$locator->set('foo', function () use ($locator) {
    $foo = new FooBarBaz($locator->get('gir'));
    $foo->setOperation('ImpendingDoom');
    return $foo;
});

// these are shared services, not separate instances
$foo_1 = $locator->get('foo');
$foo_2 = $locator->get('foo');
var_dump($foo_1 === $foo_2); // true

Who’s Doing What?

I had a quick look at some containers in PHP land to see how well they fulfill the above heuristic; my assessment is as follows:

Some notes:

  1. Laravel: The documentation calls the container an “IoC” (Inversion of Control) container but mentions “Dependency Injection” throughout, never “Service Locator.” It is clear from the container’s usage throughout the rest of the codebase that it is in fact a Service Locator, since the container itself is injected into the classes that need services. This strikes me something that can be remedied with a documenation change, since the class itself is named in a generic way. UPDATE: the project lead has informed me of the public make() method on the container that creates new instances. Although I pick nits over the naming (using $class and newInstance() instead of $abstract and make() would make things more obvious IMO) it does fulfill the above heuristic.

  2. Pimple: Interestingly, Pimple has a factory() method that will return independent instance of a service. Some may think of this as meeting the heuristic above, but I don’t think it does; you still have to define the service, after all. I suppose one might define the service as a factory for a particular class name, and name the service for the class to be factoried, but that’s not quite the same; among other things, you still have to define a service for each class you want to factory.

  3. Slim: This uses $app as a Service Locator. To his credit, Josh Lockhart does not call it either a Service Locator or a Dependency Injection container, so kudos to @codeguy for avoiding the issue altogether.

  4. Symfony DependencyInjection component: My quick review of the codebase did not reveal a way to create a new instance of a class. Instead of a missing capability, It seems more likely to me that I just did not see it. If there are any Symfony guys reading this, please let me know how to create a new instance of a class with the Symfony DependencyInjection component, and I’ll update the description.

Conclusion

According to the heuristic above, some of the things calling themselves Dependency Injection systems are probably better described as Service Locators. They might be Containers (or Inversion of Control containers) in a generic sense, but they are not Dependency Injection containers. There’s nothing wrong with not-being a DI container, but using the wrong names for things leads to confusion on a topic filled with subtleties.

Of course, I’m sure that the folks using these Service Locator systems will have plently of rationalizations for why (1) the preferred system of their particular tribe really is a Dependency Injection container no matter what made-up rules Paul wants to apply, and (2) the distinction doesn’t really matter anyway, why even bring it up, just use their preferred system. I look forward to the comments on this one. ;-)

Afterword

Are you overwhelmed by a legacy PHP codebase? Do you want to improve it, but don’t know where to start? My book, Modernizing Legacy Applications in PHP, will lead you step-by-step through a series of small, incremental changes that will dramatically improve the quality of your legacy codebase.



The "80/20" Rule Leads To The 1%

The 80/20 rules, formally known as the Pareto Principle, is a common rule-of-thumb in lots of situations. In general, it says that 80% of all effects in a particular situation comes from only 20% of the causes in that same situation. If you can eliminate the right 20% of the problem causes, then you can get rid of 80% of the effects from those problems.

Reading Taleb's "Antifragile", he points out that the complaint against "the one percent" holding 50% of the wealth in the country is a natural outgrowth of the 80/20 rule. There's no conspiracy, it just works out that way. Of the top 20%, 20% of them will hold 80% of that wealth; of that subgroup, another 20% will hold 80% of that wealth, and so on. Eventually we find that about 0.8% of a population will hold about 51.2% of the wealth, just as a rule-of-thumb. So, a baseline point of 1% of the population holding half the resources doesn't seem that out of line to me, mathematically and statistically speaking.


Lighter than Silex, and Slimmer than Slim: Aura.Web_Project, the Micro/Macro Framework

... the Aura.Web_Project package is a minimalist web-specific project framework that starts out small and grows only as you need it.

By “minimal” we mean very minimal. The project package provides only a dependency injection container, a configuration system, a router, a dispatcher, a pair of request and response objects, and a Monolog instance for logging.

This minimal implementation should not be taken as “restrictive”:

  • The DI container, coupled with the project kernel’s two-stage configuration, allows a wide range of programmatic service definitions. This means no more static calls to configure services; edit the modify stage config files, pull a service out of the container, and operate on it directly.

  • You can add any library you want into the project, not just Aura libraries, through Composer and the DI container. This means the bundle/plugin universe is made up of anything written in PHP, not just Aura-specific plugins.

  • As noted in earlier articles in this series, the router and dispatcher are completely separated from each other, and are built with iterative refactoring in mind. This means you can start with micro-framework-like closure controllers, and work your way into more complex controller objects of your own design.


Book Announcement: Modernizing Legacy Applications in PHP

There you are, working late for the second night this week. Everyone else in the office has gone home. Most of the lights are out. Yesterday it was trying to copy a feature over to a different section of the site, because marketing wanted their new client to see it there. Today, you have just finished tracking down a globals-related bug in an SQL query embedded in a page script. It revealed itself only after yesterday’s feature addition. You had to search across scores of PHP files to find it. You commit the change, push it to the common repository, and update the bug report so that QA can check on it.

Although you are relieved, you are still frustrated and worried. Who knows what break this fix will reveal? The work should not be this hard.

Your Legacy Application

You were so excited when you got the job. The interviews went well. You clearly knew more than almost everyone else there. The other developers seemed like they really needed your help and expertise. The managers gave you a new laptop and a new LCD screen so you could be as efficient as possible. It was the perfect setup. You were eager to get to work showing off your skills and abilities to make the application sing, and get the project back on schedule.

But then you checked out the code base. It was a mess. It had been architected over several years by mulitple different lead developers, and it showed. There was no consistent pattern to any of the structure. The oldest core of the system was a collection of “include” files that ran several levels deep. Later, someone had bolted on some class-oriented libraries, and third person has decided to try a framework rewrite. All of it was done in a different coding styles, with different naming conventions. The codebase is a mixed-up aggregagation of PHP, HTML, SQL, JS, and CSS, frequently all in the same file. And there are no tests at all! The “tests” are the QA team running over the site once or twice a week.

After that first day, when you felt so enthusiastic, you have been reduced to feeling like a brand-new beginner every day since. It’s humiliating. Each day has been a new frustration, a new “WTF?” moment replayed in a dozen different ways. You want very much to to improve the codebase as it is, so that you can impose some sense and reason onto it, but the code is such an overwhelming mess of spaghetti that you don’t even know where to start.

Overwhelmed and drowning in bad code that you inherited from others, you’re ready to give up.

Modernize Your Legacy Application

But what if I told you it didn’t have to be this way? What if I told you there was a specific series of small, incremental changes you could make over time to slowly make the codebase better, more modern, and thereby reduce your own sense that things are futile?

My upcoming book, “Modernizing Legacy Applications in PHP”, does exactly that. Using my talk “It Was Like That When I Got Here” as a starting point, I condense 15 years of fixing PHP codebases into a collection of specific steps to complete in order. As you apply these small fixes, each one building on the last, you will be able to restructure your codebase from a spaghetti mess to a testable, organized, modernized application, free of globals and mixed concerns.

Would you like to have that knowledge, distilled into an easy-to-understand book? Sign up on the mailing list below, and you’ll be the among the first to:

  • Find out how the book is going (and when it’s ready for purchase!)

  • Receive hints on how to improve your codebase now

  • Get a free pre-publication chapter

I promise, you’ll never receive any spam or other unwanted messages on this list. Add your email today!



Splitting The Blog

When I started this blog back in 2004, I wanted it to be a single location for me to publish on any and all topics that piqued my interest. However, events that I have planned for the future (more on that later!) make me think it would be wise to split the blog in two: one for more professional topics, and one for those that are more personal.

This existing .com blog will continue to be where I publish regarding technical, development, programming, management, and other professional topics. The new .org blog, will be "everything else," including the political and economics posts that some of my readers love and others loathe.

The non-development-related posts on this blog have already been moved over to the .org location, and will disappear from this .com site soon. Short of adding rewrite rules for *every single post* that is now only over at the .org site, I can think of nothing that will keep existing links to those posts from breaking. My apologies to those of you who are affected. Comments, etc., will be retained.

Pretty soon, I'm going to change the hosting and the theme here as well. With any luck I'll have an announcement thereafter for all the remaining technical readers.

Thanks for your patience, everybody!

UPDATE (9:48pm): Turns out it was relatively easy to select all the post IDs on the .org site and build a list of Apache "RedirectPermanent" directives on the .com site, like so:

RedirectPermanent /archives/6 http://paul-m-jones.org/archives/6

That's courtesy of Apache mod_alias, not mod_rewrite, which is nice.


Doris Lessing on Communism and Language

It is not a new thought that Communism debased language and, with language, thought. There is a Communist jargon recognizable after a single sentence. Few people in Europe have not joked in their time about “concrete steps,” “contradictions,” “the interpenetration of opposites,” and the rest.

The first time I saw that mind-deadening slogans had the power to take wing and fly far from their origins was in the 1950s when I read an article in The Times of London and saw them in use. “The demo last Saturday was irrefutable proof that the concrete situation...” Words confined to the left as corralled animals had passed into general use and, with them, ideas. One might read whole articles in the conservative and liberal press that were Marxist, but the writers did not know it. But there is an aspect of this heritage that is much harder to see.

Even five, six years ago, Izvestia, Pravda and a thousand other Communist papers were written in a language that seemed designed to fill up as much space as possible without actually saying anything. Because, of course, it was dangerous to take up positions that might have to be defended. Now all these newspapers have rediscovered the use of language. But the heritage of dead and empty language these days is to be found in academia, and particularly in some areas of sociology and psychology.

via Questions You Should Never Ask a Writer - New York Times. Looks like she has a longer form of that article here.


PSR-4 "Autoloader" Has Passed

I wrote up the initial “Towards A Package-Oriented Autoloader” essay on 26 Mar, and the first formal proposal document on 03 Apr. Counting from the date of that first formal proposal, it has taken exactly 8 months of discussions, one botched vote, one rescinded vote, an entirely new FIG workflow, and four or five rewrites to get PSR-4 passed. Maybe 8 months doesn’t sound so long when you look back on it, but while you’re in the middle of it, it’s interminable.

The core concept has not changed since the initial essay: provide a way to map the “namespace prefix” of a class to an arbitrary directory, to make directory structures shallower than they are under PSR-0. This means no more empty directories in Composer (etc) packages just to suit the strict class-to-file rule of PSR-0, which itself is descended from the Horde and PEAR standards by way of Solar, Symfony, Zend, and others.

The initial implementation of the ruleset has not changed in any major way in that entire time, although the language describing the ruleset has gone through several major variations.

For what it’s worth, the existing Aura v2 libraries are already structured according to the PSR-4 rules, and work with Composer through their own package-specific autoloader files:

Many thanks to everyone who helped bring the initial proposal to its completed state. In particular, thanks to Beau Simensen who (on what turned out to be the final major rewrite) came up with the idea of mimicking the PSR-0 language style. Also, thanks to Phil Sturgeon, who (as sponsor and vote coordinator) finally saw fit to let a vote actually go through. ;-)



Quicker, Easier, More Seductive: The Difference Between Factories, Registries, and Service Locators

In last week’s episode of this unexpected series, I said, “Go ahead and use Service Locator, but make sure each Locator contains only one type of object. This condition of restraint will keep a single locator from becoming a menace.” (I recommend “real” dependency injection containers as the preferred inversion-of-control mechanism; try the Aura.Di container.)

The “only one type of object” constraint generated some discussion regarding factories and registries. On Reddit, teresko commented:

I don’t think I have ever seen a use of Service Locator where it adhered to this rule. Hell … I am not even sure that you can call it “Service Locator” then, because it is just a smarter sounding name for Registry pattern.

When you apply this rule, what you get instead is a factory, which enforces uniqueness of an instances, that it creates.

On the blog itself, Adrian Mu asked:

In the case of the Aura’s filter package am I wrong to interpret the “rule service locator” as a “rule factory”? If no, what is the difference between a factory and a service locator as you describe it? If yes, where?

And on Twitter, Taylor Otwell asked a similar question:

that “one type” of service locator is just a “factory”, right?

The differences between factories, registries, and containers (both service locators and dependency injection containers) can be subtle and hard to grasp at first. Here’s how I keep them straight:

  • a Factory creates and returns an object, but does not retain the created object for future use;

  • a Registry retains an object instance by name for repeated use, but does not create that object;

  • a Service Locator contains a named object (a “service”) and creates it if it
    has a definition for that service; the creation logic might itself be a Factory, and the retention logic might itself be a Registry.

By way of example, I have put together an example service locator implementation along with an embedded test case. It has only two methods: set() and get():

  • The set() method takes an object name as its first argument, and
    a factory (in this case, a callable). The factories are retained in a registry (in this case, a plain-old PHP array).

  • The get() method looks in a registry of instances for the requested object by name and returns it. If the object is not there, it uses the factory for that object name and retains it in the instance registry.

Here’s an example use:

<?php
// create the service locator
$locator = new PmjonesServiceLocator;

// add a factory; this can be any callable, whether
// a closure, an object with an __invoke() method,
// a function name, a static class method, an object
// instance and method name, etc.
$locator->set('my_object', function () {
    return new VendorPackageClassName;
});

// get back an object instance; the locator
// will create and retain it the first time
$one = $locator->get('my_object');

// the next and subsequent times, the locator
// return the same instance
$two = $locator->get('my_object');
var_dump($one === $two); // true
?>

That should help illustrate the difference between a factory, a registry, and a service locator.

Again, I must stress: Service locators can easily get out of control. If you can avoid using a service locator, you should. Instead of service locators, use dependency injection proper as much as possible, even though it is more work up front. If you must use a service locator: place only one type of object in it, use multiple locators if necessary, and inject each locator instead of using static calls for access.

Afterword

In Aura, we use dependency injection everywhere, all the time. In the rare cases that a service locator makes sense, that locator follows the “only one type of object” rule, and the locator itself is injected via the DI container. This makes the codebase very clean and a lot easier to test.

If you like clean code, fully decoupled libraries, and truly independent packages, then the Aura project is for you. Download a single package and start using it in your project today, with no added dependencies.



Quicker, Easier, More Seductive: Restraining Your Service Locators

tl;dr: Go ahead and use Service Locator, but make sure each Locator contains only one type of object. This condition of restraint will keep a single locator from becoming a menace.

Quicker, Easier, More Seductive

I mentioned in passing last week, “When it comes to Inversion of Control, a Service Locator is like the Dark Side of the Force: quicker, easier, more seductive. But it gets you into trouble later on. Go with Dependency Injection whenever you can instead.” I want to elaborate on that a bit.

The question is not “Which is better and which is worse, DI container or Service Locator?” The question is “What are the proper limits on DI containers and Service Locators?”

To establish my bona fides here, I’ll point out that I used Service Locator almost exclusively for several years. Solar::dependency() was Solar’s inversion-of-control system, and it was very useful for its purpose. But knowing what I know now, it is much more a Service Locator than a Dependency Injection container. (As a side note, universal constructor was a natural outgrowth of having a Service Locator try to take on Dependency-Injection-like behaviors.)

Service locators are a great boon, and very easy to set up and use. But they also present great problems as systems grow larger. Testing becomes more and more difficult. Dependencies are obscured. If the locator is used via static methods, it’s even harder, and more obscure.

Dependency injection containers take more up-front setup, and more up-front skill and discipline on the part of the developer. They are harder to understand, and it’s harder to get the concepts that go along with DI containers (factories in particular). I’m not kidding when I say it took me a year (!) of reading Misko Hevery and getting input from more experienced DI developers before I began to get it.

It is for that reason I say that Service Locator is “quicker, easier, more seductive.” It’s not that Service Locator is bad, it’s that it gets out of control very quickly.

Restraining Your Service Locators

Service Locator still has a useful purpose, but its scope must be limited to make sure it does not grow into a monster. Other people have suggested various rules on when to use a Service Locator, and when to use a Dependency Injection container. I will not add to those rules.

Instead, I will tell you to go ahead and inject a Service Locator (no static calls!) but on one condition:

A Service Locator should contain only one type of object.

When I say “type” I don’t mean “anything my controller might happen to need.” I mean, loosely speaking, “one class of object” or “objects with the same purpose”. For example, the Aura filter system uses a locator to make different validation and sanitizing rules available. The v1 view package uses a locator for the helper objects; we have gone so far as to extract the locator and helpers to their own package in v2. Note that these locators are highly specific and handle only one type of object.

  • What if you need multiple types of objects? Inject multiple types of Service Locators.

  • What if you only need one each of several different objects? Inject those objects directly, probably via constructor arguments, instead of retrieving them from a Service Locator.

  • What if some of the objects are optional, or only needed some of the time? Inject them via setter methods.

  • What if it ends up that you genuinely need to inject lots of different locators? Take it as a sign that you need to refactor the object that needs the many different locators. Consider wrapping those parts of the object into a service, inject the locators to the service, and inject the service to the original object.

  • If you find yourself injecting a Dependency Injection container into an object so that the object can retrieve dependencies from the container, you are using the Dependency Injection container as a Service Locator. The "only one type of object" rule applies in that case. The only way around it is to stop injecting the Dependency Injection container and instead inject a particular Service Locator, or to inject the specific needed objects.

The condition of “only one type of object” is imperfect, but it should begin set you on the correct path; that is, toward being able to identify dependencies. The goal is not immediate perfection, but gradual improvement. The idea is to purposely make Service Locator a little less seductive, to add constraints to it, so that it does not become a menace later.

Afterword

If you like clean code, fully decoupled libraries, and truly independent packages, then the Aura project is for you. Download a single package and start using it in your project today, with no added dependencies.



ServiceLocator is like the Dark Side: Quicker, Easier, More Seductive

When it comes to Inversion of Control, a Service Locator is like the Dark Side of the Force: quicker, easier, more seductive. But it gets you into trouble later on. Go with Dependency Injection whenever you can instead.

A lot of things calling themselves Dependency Injection containers are actually Service Locators. If you inject the container into an object, or if you call it statically from inside an object, and the object then pulls things out of the container, the container is acting as a Service Locator.