Skip to content

Commit 23b6b27

Browse files
committed
The "When to Use it" section is now much more cautionary.
1 parent b41e449 commit 23b6b27

File tree

3 files changed

+90
-59
lines changed

3 files changed

+90
-59
lines changed

book/service-locator.markdown

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -62,30 +62,45 @@ concrete type and the process used to locate it.
6262

6363
## When to Use It
6464

65-
If a large number of places in the codebase need access to the same
66-
object and there isn't an obvious way for them to get it, this pattern
67-
is often a good solution. It's usually more flexible than a static
68-
class, and more maintainable than a <a class="gof-pattern"
69-
href="singleton.html">Singleton</a>.
70-
71-
One limitation is that the implementation of the service doesn't know
72-
*who* is using it or what for. This means it must be able to work
73-
correctly in any circumstance. For example, a class that expects to
74-
only be used during the simulation portion of the game loop and not
75-
during rendering may not work as a service -- it wouldn't be able
76-
to ensure that it's being used at the right time. So, if our class
77-
only expects to be used within a certain context, it may be safest to
78-
avoid exposing it to the world with this pattern.
65+
Anytime you make something globally accessible to every part of your program,
66+
you're asking for trouble. That's the main problem with the
67+
<a class="gof-pattern" href="singleton.html">Singleton</a> pattern, and this
68+
pattern is no different. My simplest advice for when to use a service locator
69+
is: *sparingly*.
70+
71+
Instead of using a global mechanism to give some code access to an
72+
object it needs, first consider *just passing the object to it*. That's dead
73+
simple, and it makes the coupling completely obvious. That will cover most of
74+
your needs.
75+
76+
*But...* there are some times when manually passing around an object is
77+
gratuitous or actively makes code harder to read. Some systems, like logging
78+
or memory management, shouldn't be part of a module's public API. The
79+
parameters to your rendering code should have to do with *rendering*, not
80+
stuff like logging.
81+
82+
Likewise, other systems represent facilities that are fundamentally singular
83+
in nature. Your game probably only has one audio device or display system
84+
that it can talk to. It is an ambient property of the environment, so plumbing
85+
it through ten layers of methods just so one deeply nested call can get to it
86+
is adding needless complexity to your code.
87+
88+
In those kinds of cases, this pattern can help. As we'll see, it functions
89+
as a more flexible, more configurable cousin of the singleton. When used
90+
well, it can make your codebase more flexible with little runtime cost.
7991

8092
## Keep in Mind
8193

82-
### The service is globally accessible
94+
### The service doesn't know who is locating it
8395

84-
This pattern shares a problem with the classic <a class="gof-pattern"
85-
href="singleton.html">Singleton</a> pattern: it's *global.* This is
86-
convenient for code that needs the service, but opens the door to
87-
coupling and maintenance headaches if the wrong code starts using the
88-
service.
96+
Since the locator is globally accessible, any code in the game could be
97+
requesting a service and then poking at it. This means that service must
98+
be able to work correctly in any circumstance. For example, a class that
99+
expects to only be used during the simulation portion of the game loop
100+
and not during rendering may not work as a service -- it wouldn't be able
101+
to ensure that it's being used at the right time. So, if a class
102+
expects to only be used in a certain context, it's safest to
103+
avoid exposing it to the entire world with this pattern.
89104

90105
### The service actually has to be located
91106

@@ -126,8 +141,8 @@ the service locator -- the class that ties the two together.
126141

127142
### A simple locator
128143

129-
The implementation we'll see here is about the simplest kind of
130-
service locator you'll see:
144+
The implementation here is about the simplest kind of service locator
145+
you can define:
131146

132147
<span name="di"></span>
133148

@@ -140,8 +155,8 @@ bit of jargon for a very simple idea. Say you have one class
140155
that depends on another. In our case, our `Locator` class needs an
141156
instance of the `Audio` service. Normally, the locator would be
142157
responsible for constructing that itself. Dependency injection
143-
instead says that outside code is responsible for *giving* that
144-
dependent object to our locator (i.e. injecting it into it).
158+
instead says that outside code is responsible for *injecting* that
159+
dependency into the object that needs it.
145160

146161
</aside>
147162

@@ -152,9 +167,8 @@ of our `Audio` service to use:
152167
^code 5
153168

154169
The way it "locates" is very simple: it relies on some outside code
155-
somewhere to register a service provider with it before any other code
156-
tries to use the service. When the game is starting up, it will call
157-
some code like this:
170+
to register a service provider before any tries to use the service.
171+
When the game is starting up, it calls some code like this:
158172

159173
^code 11
160174

@@ -239,8 +253,8 @@ will default to a null provider.
239253
Turning off audio is handy during development. It frees up some memory
240254
and CPU cycles. More importantly, when you break into a debugger just
241255
as a loud sound starts playing, it saves you from having your eardrums
242-
shredded. There's nothing like a scream sound effect looping every
243-
twenty milliseconds to get your blood flowing in the morning.
256+
shredded. There's nothing like twenty seconds of a scream sound effect
257+
looping at full volume to get your blood flowing in the morning.
244258

245259
</aside>
246260

code/cpp/service-locator.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,10 @@ class LoggedAudio : public Audio
7878

7979

8080
private:
81-
void log(const char* message) { /* Code to log message... */ }
81+
void log(const char* message)
82+
{
83+
// Code to log message...
84+
}
8285

8386
Audio &wrapped_;
8487
};

html/service-locator.html

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -115,26 +115,38 @@ <h2><a href="#the-pattern" name="the-pattern">The Pattern</a></h2>
115115
finding an appropriate provider while hiding both the provider&#x2019;s
116116
concrete type and the process used to locate it.</p>
117117
<h2><a href="#when-to-use-it" name="when-to-use-it">When to Use It</a></h2>
118-
<p>If a large number of places in the codebase need access to the same
119-
object and there isn&#x2019;t an obvious way for them to get it, this pattern
120-
is often a good solution. It&#x2019;s usually more flexible than a static
121-
class, and more maintainable than a <a class="gof-pattern"
122-
href="singleton.html">Singleton</a>.</p>
123-
<p>One limitation is that the implementation of the service doesn&#x2019;t know
124-
<em>who</em> is using it or what for. This means it must be able to work
125-
correctly in any circumstance. For example, a class that expects to
126-
only be used during the simulation portion of the game loop and not
127-
during rendering may not work as a service&thinsp;&mdash;&thinsp;it wouldn&#x2019;t be able
128-
to ensure that it&#x2019;s being used at the right time. So, if our class
129-
only expects to be used within a certain context, it may be safest to
130-
avoid exposing it to the world with this pattern.</p>
118+
<p>Anytime you make something globally accessible to every part of your program,
119+
you&#x2019;re asking for trouble. That&#x2019;s the main problem with the
120+
<a class="gof-pattern" href="singleton.html">Singleton</a> pattern, and this
121+
pattern is no different. My simplest advice for when to use a service locator
122+
is: <em>sparingly</em>.</p>
123+
<p>Instead of using a global mechanism to give some code access to an
124+
object it needs, first consider <em>just passing the object to it</em>. That&#x2019;s dead
125+
simple, and it makes the coupling completely obvious. That will cover most of
126+
your needs.</p>
127+
<p><em>But&hellip;</em> there are some times when manually passing around an object is
128+
gratuitous or actively makes code harder to read. Some systems, like logging
129+
or memory management, shouldn&#x2019;t be part of a module&#x2019;s public API. The
130+
parameters to your rendering code should have to do with <em>rendering</em>, not
131+
stuff like logging.</p>
132+
<p>Likewise, other systems represent facilities that are fundamentally singular
133+
in nature. Your game probably only has one audio device or display system
134+
that it can talk to. It is an ambient property of the environment, so plumbing
135+
it through ten layers of methods just so one deeply nested call can get to it
136+
is adding needless complexity to your code.</p>
137+
<p>In those kinds of cases, this pattern can help. As we&#x2019;ll see, it functions
138+
as a more flexible, more configurable cousin of the singleton. When used
139+
well, it can make your codebase more flexible with little runtime cost.</p>
131140
<h2><a href="#keep-in-mind" name="keep-in-mind">Keep in Mind</a></h2>
132-
<h3><a href="#the-service-is-globally-accessible" name="the-service-is-globally-accessible">The service is globally accessible</a></h3>
133-
<p>This pattern shares a problem with the classic <a class="gof-pattern"
134-
href="singleton.html">Singleton</a> pattern: it&#x2019;s <em>global.</em> This is
135-
convenient for code that needs the service, but opens the door to
136-
coupling and maintenance headaches if the wrong code starts using the
137-
service.</p>
141+
<h3><a href="#the-service-doesn&#x2019;t-know-who-is-locating-it" name="the-service-doesn&#x2019;t-know-who-is-locating-it">The service doesn&#x2019;t know who is locating it</a></h3>
142+
<p>Since the locator is globally accessible, any code in the game could be
143+
requesting a service and then poking at it. This means that service must
144+
be able to work correctly in any circumstance. For example, a class that
145+
expects to only be used during the simulation portion of the game loop
146+
and not during rendering may not work as a service&thinsp;&mdash;&thinsp;it wouldn&#x2019;t be able
147+
to ensure that it&#x2019;s being used at the right time. So, if a class
148+
expects to only be used in a certain context, it&#x2019;s safest to
149+
avoid exposing it to the entire world with this pattern.</p>
138150
<h3><a href="#the-service-actually-has-to-be-located" name="the-service-actually-has-to-be-located">The service actually has to be located</a></h3>
139151
<p>With a Singleton or a static class, there&#x2019;s no chance for the instance
140152
we need to <em>not</em> be available. Calling code can take for granted that
@@ -191,8 +203,8 @@ <h3><a href="#the-service-provider" name="the-service-provider">The service prov
191203
<p>Now we have an interface and an implementation. The remaining piece is
192204
the service locator&thinsp;&mdash;&thinsp;the class that ties the two together.</p>
193205
<h3><a href="#a-simple-locator" name="a-simple-locator">A simple locator</a></h3>
194-
<p>The implementation we&#x2019;ll see here is about the simplest kind of
195-
service locator you&#x2019;ll see:</p>
206+
<p>The implementation here is about the simplest kind of service locator
207+
you can define:</p>
196208
<p><span name="di"></span></p>
197209
<div class="codehilite"><pre><span class="k">class</span> <span class="nc">Locator</span>
198210
<span class="p">{</span>
@@ -217,8 +229,8 @@ <h3><a href="#a-simple-locator" name="a-simple-locator">A simple locator</a></h3
217229
that depends on another. In our case, our <code>Locator</code> class needs an
218230
instance of the <code>Audio</code> service. Normally, the locator would be
219231
responsible for constructing that itself. Dependency injection
220-
instead says that outside code is responsible for <em>giving</em> that
221-
dependent object to our locator (i.e. injecting it into it).</p>
232+
instead says that outside code is responsible for <em>injecting</em> that
233+
dependency into the object that needs it.</p>
222234
</aside>
223235

224236
<p>The static <code>getAudio()</code> function does the locating&thinsp;&mdash;&thinsp;we can call
@@ -233,9 +245,8 @@ <h3><a href="#a-simple-locator" name="a-simple-locator">A simple locator</a></h3
233245

234246

235247
<p>The way it "locates" is very simple: it relies on some outside code
236-
somewhere to register a service provider with it before any other code
237-
tries to use the service. When the game is starting up, it will call
238-
some code like this:</p>
248+
to register a service provider before any tries to use the service.
249+
When the game is starting up, it calls some code like this:</p>
239250
<div class="codehilite"><pre><span class="kt">void</span> <span class="nf">initGame</span><span class="p">()</span>
240251
<span class="p">{</span>
241252
<span class="n">ConsoleAudio</span> <span class="o">*</span><span class="n">audio</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ConsoleAudio</span><span class="p">();</span>
@@ -345,8 +356,8 @@ <h3><a href="#a-null-service" name="a-null-service">A null service</a></h3>
345356
<p>Turning off audio is handy during development. It frees up some memory
346357
and CPU cycles. More importantly, when you break into a debugger just
347358
as a loud sound starts playing, it saves you from having your eardrums
348-
shredded. There&#x2019;s nothing like a scream sound effect looping every
349-
twenty milliseconds to get your blood flowing in the morning.</p>
359+
shredded. There&#x2019;s nothing like twenty seconds of a scream sound effect
360+
looping at full volume to get your blood flowing in the morning.</p>
350361
</aside>
351362

352363
<h3><a href="#logging-decorator" name="logging-decorator">Logging decorator</a></h3>
@@ -395,7 +406,10 @@ <h3><a href="#logging-decorator" name="logging-decorator">Logging decorator</a><
395406

396407

397408
<span class="nl">private:</span>
398-
<span class="kt">void</span> <span class="n">log</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">message</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* Code to log message... */</span> <span class="p">}</span>
409+
<span class="kt">void</span> <span class="n">log</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">message</span><span class="p">)</span>
410+
<span class="p">{</span>
411+
<span class="c1">// Code to log message...</span>
412+
<span class="p">}</span>
399413

400414
<span class="n">Audio</span> <span class="o">&amp;</span><span class="n">wrapped_</span><span class="p">;</span>
401415
<span class="p">};</span>

0 commit comments

Comments
 (0)