@@ -16,23 +16,23 @@ the entire game.
1616
1717For our example, we'll consider audio. It doesn't have quite the reach of
1818something lower-level like a memory allocator, but it still touches a bunch of
19- game systems: A falling rock hits the ground with a crash (physics). A sniper
19+ game systems. A falling rock hits the ground with a crash (physics). A sniper
2020NPC fires his rifle and a shot rings out (AI). The user selects a menu item with
2121a beep of confirmation (user interface).
2222
23- Each of these places will need to be able to call into the audio system, with
23+ Each of these places will need to be able to call into the audio system with
2424something like one of these:
2525
2626^code 15
2727
2828Either gets us where we're trying to go, but we stumbled into some sticky
2929coupling along the way. Every place in the game calling into our audio system
30- directly references the concrete AudioSystem class and the mechanism for
30+ directly references the concrete ` AudioSystem ` class and the mechanism for
3131accessing it -- either as a static class or a <a class="gof-pattern"
32- href="singleton .html">singleton</a >.
32+ href="Singleton .html">singleton</a >.
3333
3434These callsites, of course, have to be coupled to * something* in order to make a
35- sound play, but letting them poke directly at the concrete audio implementation
35+ sound play, but letting them poke at the concrete audio implementation directly
3636is like giving a hundred strangers directions to your house just so they can
3737drop a letter on your doorstep. Not only is it a little bit * too* personal, it's
3838a real pain when you move and you have to tell each person the new directions.
@@ -45,7 +45,7 @@ or some other "representation" of ourselves instead. By having callers go
4545through the book to find us, we have * a convenient single place where we control
4646how we're found.*
4747
48- This is the Service Locator pattern in a nutshell: it decouples code that needs
48+ This is the Service Locator pattern in a nutshell -- it decouples code that needs
4949a service from both * who* it is (the concrete implementation type) and * where*
5050it is (how we get to the instance of it).
5151
5959
6060## When to Use It
6161
62- Anytime you make something globally accessible to every part of your program ,
62+ Anytime you make something globally accessible,
6363you're asking for trouble. That's the main problem with the <a
6464class="gof-pattern" href="singleton.html">Singleton</a > pattern, and this
6565pattern is no different. My simplest advice for when to use a service locator
6666is: * sparingly* .
6767
6868Instead of using a global mechanism to give some code access to an object it
69- needs, first consider * just passing the object to it* . That's dead simple, and
69+ needs, first consider * passing the object to it instead * . That's dead simple, and
7070it makes the coupling completely obvious. That will cover most of your needs.
7171
7272* But...* there are some times when manually passing around an object is
@@ -81,7 +81,7 @@ through ten layers of methods just so one deeply nested call can get to it is
8181adding needless complexity to your code.
8282
8383In those kinds of cases, this pattern can help. As we'll see, it functions as a
84- more flexible, more configurable cousin of the singleton . When used <span
84+ more flexible, more configurable cousin of the Singleton pattern . When used <span
8585name="well">well</span >, it can make your codebase more flexible with little
8686runtime cost.
8787
@@ -97,7 +97,7 @@ Singleton pattern with worse runtime performance.
9797The core difficulty with a service locator is that it takes a dependency -- a
9898bit of coupling between two pieces of code -- and defers wiring it up until
9999runtime. This gives you flexibility, but the price you pay is that it's harder
100- to understand what your dependencies are just by reading the code.
100+ to understand what your dependencies are by reading the code.
101101
102102### The service actually has to be located
103103
@@ -110,11 +110,11 @@ guarantee that we'll always get *some* service when you need it.
110110### The service doesn't know who is locating it
111111
112112Since the locator is globally accessible, any code in the game could be
113- requesting a service and then poking at it. This means that service must be able
114- to work correctly in any circumstance. For example, a class that expects to only
115- be used during the simulation portion of the game loop and not during rendering
113+ requesting a service and then poking at it. This means that the service must be able
114+ to work correctly in any circumstance. For example, a class that expects to
115+ be used only during the simulation portion of the game loop and not during rendering
116116may not work as a service -- it wouldn't be able to ensure that it's being used
117- at the right time. So, if a class expects to only be used in a certain context,
117+ at the right time. So, if a class expects to be used only in a certain context,
118118it's safest to avoid exposing it to the entire world with this pattern.
119119
120120## Sample Code
@@ -159,26 +159,26 @@ define:
159159The technique this uses is called * dependency injection* , an awkward bit of
160160jargon for a very simple idea. Say you have one class that depends on another.
161161In our case, our ` Locator ` class needs an instance of the ` Audio ` service.
162- Normally, the locator would be responsible for constructing that itself.
162+ Normally, the locator would be responsible for constructing that instance itself.
163163Dependency injection instead says that outside code is responsible for
164164* injecting* that dependency into the object that needs it.
165165
166166</aside >
167167
168- The static ` getAudio() ` function does the locating -- we can call it from
169- anywhere in the codebase and it will give us back an instance of our ` Audio `
168+ The static ` getAudio() ` function does the locating. We can call it from
169+ anywhere in the codebase, and it will give us back an instance of our ` Audio `
170170service to use:
171171
172172^code 5
173173
174- The way it "locates" is very simple: it relies on some outside code to register
175- a service provider before any tries to use the service. When the game is
174+ The way it "locates" is very simple -- it relies on some outside code to register
175+ a service provider before any code tries to use the service. When the game is
176176starting up, it calls some code like this:
177177
178178^code 11
179179
180180The key part to notice here is that our ` someGameCode() ` function isn't aware of
181- the concrete ` ConsoleAudio ` class, just the abstract ` Audio ` interface. Equally
181+ the concrete ` ConsoleAudio ` class; it is only aware of the abstract ` Audio ` interface. Equally
182182important, not even the * locator* class is coupled to the concrete service
183183provider. The * only* place in code that knows about the actual concrete class is
184184the initialization function that registers the service.
@@ -200,18 +200,18 @@ If the calling code doesn't check that, we're going to crash the game.
200200
201201<aside name =" temporal " >
202202
203- I sometimes hear this called "temporal coupling": two separate pieces of code
203+ I sometimes hear this called "temporal coupling" -- two separate pieces of code
204204that must be called in the right order for the program to work correctly. All
205205stateful software has some degree of this, but as with other kinds of coupling,
206- reducing it makes the codebase easier to manage.
206+ reducing temporal coupling makes the codebase easier to manage.
207207
208208</aside >
209209
210210Fortunately, there's another design pattern called "Null Object" that we can use
211211to address this. The basic idea is that in places where we would return ` NULL `
212212when we fail to find or create an object, we instead return a special object
213213that implements the same interface as the desired object. Its implementation
214- basically does nothing, but allows code that receives the object to safely
214+ basically does nothing, but it allows code that receives the object to safely
215215continue on as if it had received a "real" one.
216216
217217To use this, we'll define another "null" service provider:
@@ -233,8 +233,8 @@ reference is a hint to users of the code that they can expect to always get a
233233valid object back.
234234
235235The other thing to notice is that we're checking for ` NULL ` in the ` provide() `
236- function instead of the accessor. That requires us to call ` initialize() ` early
237- on to make sure that the Locator initially correctly defaults to the null
236+ function instead of checking for the accessor. That requires us to call ` initialize() ` early
237+ on to make sure that the locator initially correctly defaults to the null
238238provider. In return, it moves the branch out of ` getAudio() ` , which will save us
239239a couple of cycles every time the service is accessed.
240240
@@ -246,7 +246,7 @@ object.
246246
247247This is also useful for * intentionally* failing to find services. If we want to
248248<span name =" disable " >disable</span > a system temporarily, we now have an easy
249- way to do so: simply don't register a provider for the service and the locator
249+ way to do so: simply don't register a provider for the service, and the locator
250250will default to a null provider.
251251
252252<aside name =" disable " >
@@ -262,17 +262,17 @@ your blood flowing in the morning.
262262### Logging decorator
263263
264264Now that our system is pretty robust, let's discuss another refinement this
265- pattern lets us do: decorated services. I'll explain with an example.
265+ pattern lets us do -- decorated services. I'll explain with an example.
266266
267267During development, a little logging when interesting events occur can help you
268268figure out what's going on under the hood of your game engine. If you're working
269269on AI, you'd like to know when an entity changes AI states. If you're the sound
270270programmer, you may want a record of every sound as it plays so you can check
271271that they trigger in the right order.
272272
273- The typical solution is to just litter the code with calls to some ` log() `
274- function. Unfortunately, that replaces one problem with another: now we have
275- * too much* logging. The AI coder really doesn't care when sounds are playing,
273+ The typical solution is to litter the code with calls to some ` log() `
274+ function. Unfortunately, that replaces one problem with another -- now we have
275+ * too much* logging. The AI coder doesn't care when sounds are playing,
276276and the sound person doesn't care about AI state transitions, but now they both
277277have to wade through each other's messages.
278278
@@ -286,26 +286,26 @@ define another audio service provider implementation like this:
286286^code 12
287287
288288As you can see, it wraps another audio provider and exposes the same interface.
289- It forwards the actual audio behavior to the inner provider, but also logs each
289+ It forwards the actual audio behavior to the inner provider, but it also logs each
290290sound call. If a programmer wants to enable audio logging, they call this:
291291
292292^code 13
293293
294- Now any calls to the audio service will be logged before continuing as before.
294+ Now, any calls to the audio service will be logged before continuing as before.
295295And, of course, this plays nicely with our null service, so you can both
296296* disable* audio and yet still log the sounds that it * would* play if sound were
297297enabled.
298298
299299## Design Decisions
300300
301- We've covered a typical implementation, but there's a couple of ways that it can
302- vary, based on differing answers to a few core questions:
301+ We've covered a typical implementation, but there are a couple of ways that it can
302+ vary based on differing answers to a few core questions:
303303
304304### How is the service located?
305305
306306 * ** Outside code registers it:**
307307
308- This is the mechanism our sample code uses to locate the service, and is the
308+ This is the mechanism our sample code uses to locate the service, and it is the
309309 most common design I see in games:
310310
311311 * * It's fast and simple.* The ` getAudio() ` function simply returns a
@@ -316,11 +316,11 @@ vary, based on differing answers to a few core questions:
316316 accessing the game's controllers. We have two concrete providers: one
317317 for regular games and one for playing online. The online provider passes
318318 controller input over the network so that, to the rest of the game,
319- remote players appear to just be using local controllers.
319+ remote players appear to be using local controllers.
320320
321321 To make this work, the online concrete provider needs to know the IP
322322 address of the other remote player. If the locator itself was
323- constructing the object, how would it know what to pass in? The locator
323+ constructing the object, how would it know what to pass in? The ` Locator `
324324 class doesn't know anything about online at all, much less some other
325325 user's IP address.
326326
@@ -361,7 +361,7 @@ vary, based on differing answers to a few core questions:
361361 unavailable.
362362
363363 * * You can't change the service easily.* This is the major downside. Since
364- the binding happens at build time, any time you want to change the
364+ the binding happens at build time, anytime you want to change the
365365 service, you've got to recompile and restart the game.
366366
367367 * ** Configure it at runtime:**
@@ -377,23 +377,23 @@ vary, based on differing answers to a few core questions:
377377 the type system at runtime. For example, we could find a class with a given
378378 name, find its constructor, and then invoke it to create an instance.
379379
380- Dynamically- typed languages like Lisp, Smalltalk, and Python get this by
380+ Dynamically typed languages like Lisp, Smalltalk, and Python get this by
381381 their very nature, but newer static languages like C# and Java also support
382382 it.
383383
384384 </aside >
385385
386386 Typically, this means loading a configuration file that identifies the
387- provider, then using reflection to instantiate that class at runtime. This
387+ provider and then using reflection to instantiate that class at runtime. This
388388 does a few things for us:
389389
390390 * * We can swap out the service without recompiling.* This is a little more
391- flexible than a compile-time bound service, but not quite as flexible as
391+ flexible than a compile-time- bound service, but not quite as flexible as
392392 a registered one where you can actually change the service while the
393393 game is running.
394394
395395 * * Non-programmers can change the service.* This is nice for when the
396- designers want to be able to turn certain game features on and off, but
396+ designers want to be able to turn certain game features on and off but
397397 aren't comfortable mucking through source code. (Or, more likely, the
398398 * coders* aren't comfortable with them mucking through it.)
399399
@@ -404,8 +404,8 @@ vary, based on differing answers to a few core questions:
404404
405405 This is one of the reasons this model is appealing over in enterprise
406406 web-land: you can deploy a single app that works on different server
407- set-ups just by changing some configs. It's less useful in games.
408- Console hardware is pretty well standardized, and even PC games target a
407+ setups just by changing some configs. It's less useful in games.
408+ Console hardware is pretty well- standardized, and even PC games target a
409409 certain baseline specification.
410410
411411 * * It's complex.* Unlike the previous solutions, this one is pretty
@@ -422,7 +422,7 @@ vary, based on differing answers to a few core questions:
422422 CPU cycles on something that doesn't improve the player's game
423423 experience.
424424
425- ### What happens if service could not be located?
425+ ### What happens if the service can't be located?
426426
427427 * ** Let the user handle it:**
428428
@@ -470,7 +470,7 @@ vary, based on differing answers to a few core questions:
470470 caller is that I will not be passed ` NULL ` ."
471471
472472 Assertions help us track down bugs as soon as the game does something
473- unexpected, and not later when that error finally manifests as something
473+ unexpected, not later when that error finally manifests as something
474474 visibly wrong to the user. They are fences in your codebase, corralling bugs
475475 so that they can't escape from the code that created them.
476476
@@ -479,15 +479,15 @@ vary, based on differing answers to a few core questions:
479479 If the service isn't located, the game stops before any subsequent code
480480 tries to use it. The ` assert() ` call there doesn't solve the problem of
481481 failing to locate the service, but it does make it clear whose problem it
482- is. By asserting here, we say, "failing to locate a service is a bug in the
482+ is. By asserting here, we say, "Failing to locate a service is a bug in the
483483 locator."
484484
485485 So what does this do for us?
486486
487487 * * Users don't need to handle a missing service.* Since a single service
488488 may be used in hundreds of places, this can be a significant code
489489 saving. By declaring it the locator's job to always provide a service,
490- we spare the users of it from having to pick up that slack.
490+ we spare the users of the service from having to pick up that slack.
491491
492492 * * The game is going to halt if the service can't be found.* On the off
493493 chance that a service really can't be found, the game is going to halt.
@@ -515,7 +515,7 @@ vary, based on differing answers to a few core questions:
515515 missing service. Say the game uses a service to access some data and
516516 then make a decision based on it. If we've failed to register the real
517517 service and that code gets a null service instead, the game may not
518- behave like we want. <span name =" null " >It</span > will take some work to
518+ behave how we want. <span name =" null " >It</span > will take some work to
519519 trace that issue back to the fact that a service wasn't there when we
520520 thought it would be.
521521
@@ -534,7 +534,7 @@ chances of a service failing to be found by then are pretty slim.
534534On a larger team, I encourage you to throw a null service in. It doesn't take
535535much effort to implement, and can spare you from some downtime during
536536development when a service isn't available. It also gives you an easy way to
537- turn a service off if it's buggy or is just distracting you from what you're
537+ turn off a service if it's buggy or is just distracting you from what you're
538538working on.
539539
540540### What is the scope of the service?
@@ -558,9 +558,9 @@ There are advantages either way:
558558 one.
559559
560560 * * We lose control over where and when the service is used.* This is the
561- obvious cost of making something global: anything can get to it. The <a
561+ obvious cost of making something global -- anything can get to it. The <a
562562 class="gof-pattern" href="singleton.html">Singleton</a > chapter has a
563- full cast of characters for the horrorshow that global scope can spawn.
563+ full cast of characters for the horror show that global scope can spawn.
564564
565565 * ** If access is restricted to a class:**
566566
0 commit comments