{
    "href": "/post/2013/11/25/quicker-easier-more-seductive-restraining-your-service-locators/",
    "relId": "2013/11/25/quicker-easier-more-seductive-restraining-your-service-locators",
    "title": "Quicker, Easier, More Seductive: Restraining Your Service Locators",
    "author": "pmjones",
    "markup": "html",
    "tags": [
        {
            "href": "/tag/aura/",
            "relId": "aura",
            "title": "Aura",
            "author": null,
            "created": "2020-09-14 21:51:57 UTC",
            "updated": [
                "2020-09-14 21:51:57 UTC"
            ],
            "markup": "markdown"
        },
        {
            "href": "/tag/php/",
            "relId": "php",
            "title": "PHP",
            "author": null,
            "created": null,
            "updated": [],
            "markup": "markdown"
        },
        {
            "href": "/tag/programming/",
            "relId": "programming",
            "title": "Programming",
            "author": null,
            "created": null,
            "updated": [],
            "markup": "markdown"
        }
    ],
    "created": "2013-11-25 16:37:11 UTC",
    "updated": [
        "2013-11-25 16:37:11 UTC"
    ],
    "html": "<p>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.</p>\n<h3>Quicker, Easier, More Seductive</h3>\n<p>I mentioned in passing <a href=\"http://paul-m-jones.com/archives/4789\">last week</a>, \u201cWhen 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.\u201d  I want to elaborate on that a bit.</p>\n<p>The question is not \u201cWhich is better and which is worse, DI container or Service Locator?\u201d The question is \u201cWhat are the proper limits on DI containers and Service Locators?\u201d</p>\n<p>To establish my bona fides here, I\u2019ll point out that I used Service Locator almost exclusively for several years. <a href=\"http://solarphp.com/apidoc/class.Solar.dependency\">Solar::dependency()</a> was Solar\u2019s 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, <a href=\"http://solarphp.com/manual/appendix-standards.constructor\">universal constructor</a> was a natural outgrowth of having a Service Locator try to take on Dependency-Injection-like behaviors.)</p>\n<p>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\u2019s even harder, and more obscure.</p>\n<p>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\u2019s harder to get the concepts that go along with DI containers (factories in particular). I\u2019m 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.</p>\n<p>It is for that reason I say that Service Locator is \u201cquicker, easier, more seductive.\u201d  It\u2019s not that Service Locator is bad, it\u2019s that it gets out of control very quickly.</p>\n<h3>Restraining Your Service Locators</h3>\n<p>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.</p>\n<p>Instead, I will tell you to go ahead and inject a Service Locator (no static calls!) but on one condition:</p>\n<p><strong>A Service Locator should contain only one type of object.</strong></p>\n<p>When I say \u201ctype\u201d I don\u2019t mean \u201canything my controller might happen to need.\u201d  I mean, loosely speaking, \u201cone class of object\u201d or \u201cobjects with the same purpose\u201d.  For example, the Aura <a href=\"https://github.com/auraphp/Aura.Filter\">filter</a> system uses a <a href=\"https://github.com/auraphp/Aura.Filter/blob/develop/src/Aura/Filter/RuleLocator.php\">locator</a> to make different validation and sanitizing rules available.  The <a href=\"https://github.com/auraphp/Aura.View\">v1 view package</a> uses a <a href=\"https://github.com/auraphp/Aura.View/blob/develop/src/Aura/View/HelperLocator.php\">locator</a> for the helper objects; we have gone so far as to extract the locator and helpers to <a href=\"https://github.com/auraphp/Aura.Html\">their own package</a> in v2.  Note that these locators are highly specific and handle only one type of object.</p>\n<ul>\n<li>\n<p>What if you need multiple types of objects?  Inject multiple types of Service Locators.</p>\n</li>\n<li>\n<p>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.</p>\n</li>\n<li>\n<p>What if some of the objects are optional, or only needed some of the time?  Inject them via setter methods.</p>\n</li>\n<li>\n<p>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.</p>\n</li>\n<li>\n<p>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.</p>\n</li>\n</ul>\n<p>The condition of \u201conly one type of object\u201d 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.</p>\n<h3>Afterword</h3>\n<p>If you like clean code, fully decoupled libraries, and truly independent packages, then <a href=\"http://auraphp.com\">the Aura project</a> is for you. Download a single package and start using it in your project today, with no added dependencies.</p>\n<hr>\n<p class=\"reddit-links\">Read the Reddit discussion about this post <a href=\"https://www.reddit.com/r/PHP/comments/1rf6xz/quicker_easier_more_seductive_restraining_your/\">here</a>.</p>\n"
}
