needle 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/benchmarks/instantiation.rb +39 -0
- data/doc/faq/faq.yml +137 -102
- data/doc/manual-html/chapter-1.html +15 -19
- data/doc/manual-html/chapter-2.html +52 -23
- data/doc/manual-html/chapter-3.html +7 -9
- data/doc/manual-html/chapter-4.html +6 -8
- data/doc/manual-html/chapter-5.html +16 -19
- data/doc/manual-html/chapter-6.html +31 -8
- data/doc/manual-html/chapter-7.html +19 -8
- data/doc/manual-html/chapter-8.html +8 -11
- data/doc/manual-html/chapter-9.html +7 -6
- data/doc/manual-html/index.html +5 -3
- data/doc/manual/manual.yml +2 -1
- data/doc/manual/parts/01_alternatives.txt +2 -1
- data/doc/manual/parts/02_services.txt +33 -0
- data/doc/manual/parts/logging_logfactory.txt +9 -0
- data/doc/manual/parts/models_models.txt +4 -0
- data/doc/manual/parts/models_pipelines.txt +1 -0
- data/lib/needle/container.rb +64 -19
- data/lib/needle/definition-context.rb +34 -6
- data/lib/needle/lifecycle/multiton.rb +64 -0
- data/lib/needle/lifecycle/singleton.rb +2 -2
- data/lib/needle/lifecycle/threaded.rb +4 -3
- data/lib/needle/pipeline/collection.rb +10 -1
- data/lib/needle/pipeline/element.rb +6 -0
- data/lib/needle/registry.rb +18 -5
- data/lib/needle/service-point.rb +21 -10
- data/lib/needle/version.rb +1 -1
- data/test/lifecycle/tc_multiton.rb +43 -0
- data/test/lifecycle/tc_singleton.rb +18 -2
- data/test/lifecycle/tc_threaded.rb +12 -6
- data/test/pipeline/tc_collection.rb +26 -2
- data/test/services.rb +8 -0
- data/test/tc_container.rb +82 -0
- data/test/tc_definition_context.rb +63 -7
- data/test/tc_registry.rb +13 -1
- data/test/tc_service_point.rb +49 -0
- metadata +4 -2
@@ -14,8 +14,8 @@
|
|
14
14
|
</div>
|
15
15
|
</td><td valign='middle' align='right'>
|
16
16
|
<div class="info">
|
17
|
-
Needle Version: <strong>1.
|
18
|
-
Manual Last Updated: <strong>2004-11-
|
17
|
+
Needle Version: <strong>1.2.0</strong><br />
|
18
|
+
Manual Last Updated: <strong>2004-11-18 15:36 GMT</strong>
|
19
19
|
</div>
|
20
20
|
</td></tr>
|
21
21
|
</table>
|
@@ -220,8 +220,7 @@
|
|
220
220
|
|
221
221
|
|
222
222
|
<div class="section">
|
223
|
-
<p>Needle is a dependency injection (also, inversion of control) container for <a href="http://www.ruby-lang.org">Ruby</a
|
224
|
-
</p>
|
223
|
+
<p>Needle is a dependency injection (also, inversion of control) container for <a href="http://www.ruby-lang.org">Ruby</a>.</p>
|
225
224
|
</div>
|
226
225
|
|
227
226
|
|
@@ -239,7 +238,6 @@
|
|
239
238
|
<p>But what, <em>specifically</em>, can Needle do for you?</p>
|
240
239
|
|
241
240
|
<p>Try these on for size:</p>
|
242
|
-
|
243
241
|
<ul>
|
244
242
|
<li><a href="#logexec">Log Method Execution</a></li>
|
245
243
|
<li><a href="#refsvc">Reference Another Service</a></li>
|
@@ -255,6 +253,7 @@
|
|
255
253
|
|
256
254
|
<p>Consider the following code, demonstrating how this would be done without Needle:</p>
|
257
255
|
|
256
|
+
|
258
257
|
<pre>
|
259
258
|
def foo( arg1, arg2 )
|
260
259
|
@log.debug( "in foo with #{arg1} and #{arg2}" ) if @log.debug?
|
@@ -267,31 +266,31 @@
|
|
267
266
|
raise
|
268
267
|
end
|
269
268
|
</pre>
|
270
|
-
|
271
269
|
<p>Now, multiply that by the number of methods in your class… the logging messages quickly overpower the rest of the code, and detract from the flow of your program. This makes your program harder to debug, test, and maintain.</p>
|
272
270
|
|
273
271
|
<p>Now, consider the same method using Needle’s integrated logging framework…</p>
|
274
272
|
|
273
|
+
|
275
274
|
<pre>
|
276
275
|
def foo( arg1, arg2 )
|
277
276
|
...
|
278
277
|
return the_result_of_the_method
|
279
278
|
end
|
280
279
|
</pre>
|
281
|
-
|
282
280
|
<p>Then, when you define the service that you want to add the logging to:</p>
|
283
281
|
|
282
|
+
|
284
283
|
<pre>
|
285
284
|
registry.register( :service_name_here ) { |reg| ... }
|
286
285
|
registry.intercept( :service_name_here ).with! { logging_interceptor }
|
287
286
|
</pre>
|
288
|
-
|
289
287
|
<p>That’s right. There’s no explicit logging code in there. Instead, you just tell Needle that the methods of the class should be logged, and away it goes. This has the added benefit of allowing your objects to be unit tested, without spewing log messages everywhere.</p>
|
290
288
|
|
291
289
|
<h3>Reference Another Service <a name="#refsvc"></a></h3>
|
292
290
|
|
293
291
|
<p>Invariably in a large application services will reference other services. This is typically accomplished through something like this:</p>
|
294
292
|
|
293
|
+
|
295
294
|
<pre>
|
296
295
|
class Component
|
297
296
|
...
|
@@ -306,11 +305,11 @@
|
|
306
305
|
...
|
307
306
|
end
|
308
307
|
</pre>
|
309
|
-
|
310
308
|
<p>Whether the lookup is done lazily, as shown above, or when the class is first instantiated is irrelevant. The point is that you either have to implement a bunch of code to look up a service based on some criteria, or you hard code the class of the service (which creates tight coupling and makes things like unit testing harder).</p>
|
311
309
|
|
312
310
|
<p>With Needle, you just declare a setter for the service, and then tell Needle that the class depends on the other service:</p>
|
313
311
|
|
312
|
+
|
314
313
|
<pre>
|
315
314
|
class Component
|
316
315
|
attr_writer :service
|
@@ -327,7 +326,6 @@
|
|
327
326
|
c
|
328
327
|
end
|
329
328
|
</pre>
|
330
|
-
|
331
329
|
<p>Then, when your service is instantiated, Needle will automatically look for and instantiate the dependencies for you. This makes for cleaner code, and looser coupling between services.</p>
|
332
330
|
|
333
331
|
<h3>Unit Testing <a name="#unittest"></a></h3>
|
@@ -338,15 +336,16 @@
|
|
338
336
|
|
339
337
|
<p>Consider this tightly coupled example:</p>
|
340
338
|
|
339
|
+
|
341
340
|
<pre>
|
342
341
|
def foo( args )
|
343
342
|
@some_dependency ||= MyNewDependency.new
|
344
343
|
@some_dependency.do_something(args)
|
345
344
|
end
|
346
345
|
</pre>
|
347
|
-
|
348
346
|
<p>It is impossible to test the method <code>#foo</code> without also testing the MyNewDependency class. However, if the <code>@some_dependency</code> object is made a property that is set externally, you can replace it at test time with a blank:</p>
|
349
347
|
|
348
|
+
|
350
349
|
<pre>
|
351
350
|
attr_writer :some_dependency
|
352
351
|
|
@@ -354,9 +353,9 @@
|
|
354
353
|
@some_dependency.do_something( args )
|
355
354
|
end
|
356
355
|
</pre>
|
357
|
-
|
358
356
|
<p>The unit test would become something like this:</p>
|
359
357
|
|
358
|
+
|
360
359
|
<pre>
|
361
360
|
def test_foo
|
362
361
|
@obj.some_dependecy = MyMockDependency.new
|
@@ -364,7 +363,6 @@
|
|
364
363
|
assert @obj.is_in_some_state
|
365
364
|
end
|
366
365
|
</pre>
|
367
|
-
|
368
366
|
<h3>Lifecycle Management <a name="#lifecycle"></a></h3>
|
369
367
|
|
370
368
|
<p>Singleton objects are a fact of life in complex systems. The singleton design pattern is powerful and useful. However, using the Singleton mixin, or declaring methods at the class level, can make your code difficult to unit test since the state of such objects cannot be easily reset.</p>
|
@@ -389,14 +387,14 @@
|
|
389
387
|
|
390
388
|
<div class="section">
|
391
389
|
<p>Needle is not the only fish in the dependency-injection pond, even when it comes to Ruby. Other containers at your disposal include:</p>
|
392
|
-
|
393
390
|
<ul>
|
394
391
|
<li><a href="http://copland.rubyforge.org">Copland</a>. Copland aims to be an “application framework”, taking something of a heavy-weight approach to DI. In so doing, it provides functionality that Needle does not, but at the cost of performance. It also uses external (YAML) configuration files. It is inspired by a Java framework (<a href="http://jakarta.apache.org/hivemind">HiveMind</a>), and so has a vaguely Java-ish flavor to it.</li>
|
395
392
|
<li><a href="http://www.picocontainer.org/Rico">Rico</a>. Rico is another project inspired by a Java project (<a href="http://www.picocontainer.org">PicoContainer</a>). It is very lean, and appears to be experimental.</li>
|
396
393
|
<li><a href="http://sourceforge.jp/projects/nihohi/">Tudura</a>. I do not have any information on this project, as the information is all in Japanese. If someone with more information about Tudura would like to step forward, I’d be happy to post a summary here.</li>
|
394
|
+
<li><a href="http://redshift.sourceforge.net/mindi/">MinDI</a> is a recent contender that takes a very novel approach to dependency injection. Instead of instantiating a container and adding services to it, you declare a class, mixin a DI module, and add services to the class as methods, thereby making the class the container.</li>
|
397
395
|
</ul>
|
398
396
|
|
399
|
-
<p>There is, at the time of this writing, at least one other project on RubyForge devoted to DI, although it has no public releases yet.</p>
|
397
|
+
<p>There is, at the time of this writing, at least one other project on RubyForge devoted to <span class="caps">DI </span>(<a href="http://rubyforge.org/projects/pith">Pith</a>), although it has no public releases yet.</p>
|
400
398
|
|
401
399
|
<p>So, which one should you choose? It comes down to an issue of personal preference, mostly, but also one of what you are wanting to accomplish. Needle excels at providing an unobtrusive, light-weight container for managing your dependencies. The cost of it being light-weight is that there is functionality it does not provide, which other containers may. If you really need that missing functionality, you are required to either implement it yourself, or select a different container.</p>
|
402
400
|
|
@@ -417,8 +415,7 @@
|
|
417
415
|
|
418
416
|
<p>This manual (in any form, be it source or otherwise) and the scripts and templates used to generate it, are all distributed under the <a href="http://creativecommons.org">Creative Commons</a> <a href="http://creativecommons.org/licenses/by-sa/2.0">Attribution-ShareAlike</a> license.</p>
|
419
417
|
|
420
|
-
<p>If you desire permission to use either Needle or the manual in a manner incompatible with these licenses, please contact the copyright holder (<a href="mailto:jgb3@email.byu.edu">Jamis Buck</a>) in order to negotiate a more compatible license
|
421
|
-
</p>
|
418
|
+
<p>If you desire permission to use either Needle or the manual in a manner incompatible with these licenses, please contact the copyright holder (<a href="mailto:jgb3@email.byu.edu">Jamis Buck</a>) in order to negotiate a more compatible license.</p>
|
422
419
|
</div>
|
423
420
|
|
424
421
|
|
@@ -431,8 +428,7 @@
|
|
431
428
|
|
432
429
|
|
433
430
|
<div class="section">
|
434
|
-
<p>Mailing lists, bug trackers, feature requests, and public forums are available (courtesy of <a href="http://rubyforge.org">RubyForge</a>) at Needle’s RubyForge project page. Just direct your browser to <a href="http://rubyforge.org/projects/needle">http://rubyforge.org/projects/needle</a
|
435
|
-
</p>
|
431
|
+
<p>Mailing lists, bug trackers, feature requests, and public forums are available (courtesy of <a href="http://rubyforge.org">RubyForge</a>) at Needle’s RubyForge project page. Just direct your browser to <a href="http://rubyforge.org/projects/needle">http://rubyforge.org/projects/needle</a>.</p>
|
436
432
|
</div>
|
437
433
|
|
438
434
|
|
@@ -14,8 +14,8 @@
|
|
14
14
|
</div>
|
15
15
|
</td><td valign='middle' align='right'>
|
16
16
|
<div class="info">
|
17
|
-
Needle Version: <strong>1.
|
18
|
-
Manual Last Updated: <strong>2004-11-
|
17
|
+
Needle Version: <strong>1.2.0</strong><br />
|
18
|
+
Manual Last Updated: <strong>2004-11-18 15:36 GMT</strong>
|
19
19
|
</div>
|
20
20
|
</td></tr>
|
21
21
|
</table>
|
@@ -222,8 +222,7 @@
|
|
222
222
|
<div class="section">
|
223
223
|
<p>The registry is at the heart of any dependency-injected application or library. All services are registered with the registry, so that when an application needs an instance of a particular service, it may obtain that service reference by querying the registry.</p>
|
224
224
|
|
225
|
-
<p>In order to use Needle, you only really <em>need</em> to understand how to create and manipulate registry objects
|
226
|
-
</p>
|
225
|
+
<p>In order to use Needle, you only really <em>need</em> to understand how to create and manipulate registry objects.</p>
|
227
226
|
</div>
|
228
227
|
|
229
228
|
|
@@ -238,44 +237,43 @@
|
|
238
237
|
<div class="section">
|
239
238
|
<p>Creating a registry is as simple as calling <code>Needle::Registry.new</code>. This will give you a new registry object, bootstrapped to contain a few general services.</p>
|
240
239
|
|
240
|
+
|
241
241
|
<pre>
|
242
242
|
require 'needle'
|
243
243
|
|
244
244
|
registry = Needle::Registry.new
|
245
245
|
</pre>
|
246
|
-
|
247
246
|
<p>Once you have the reference to the registry, you can register services with it, create new namespaces in it, and so forth.</p>
|
248
247
|
|
249
248
|
<p>Alternatively, you can pass a block to <code>#new</code>:</p>
|
250
249
|
|
250
|
+
|
251
251
|
<pre>
|
252
252
|
registry = Needle::Registry.new do |r|
|
253
253
|
...
|
254
254
|
end
|
255
255
|
</pre>
|
256
|
-
|
257
256
|
<p>The parameter to the block will be a reference to the registry. This allows you to register services with the registry as soon as it is created.</p>
|
258
257
|
|
259
258
|
<p>Another convenience method is <code>#define!</code>:</p>
|
260
259
|
|
260
|
+
|
261
261
|
<pre>
|
262
262
|
registry = Needle::Registry.define! do
|
263
263
|
...
|
264
264
|
end
|
265
265
|
</pre>
|
266
|
-
|
267
266
|
<p>This block accepts no parameters, and evaluates the block as if it were passed to <code>Registry#define!</code> (see below).</p>
|
268
267
|
|
269
268
|
<p>There can be problems with using <code>define!</code>, however, since it uses <code>instance_eval</code> to evaluate the block within the context of another object. If you find yourself running into scoping issues, you might want to consider using <code>#define</code>:</p>
|
270
269
|
|
270
|
+
|
271
271
|
<pre>
|
272
272
|
registry = Needle::Registry.define do |b|
|
273
273
|
...
|
274
274
|
end
|
275
275
|
</pre>
|
276
|
-
|
277
|
-
<p>This block accepts a single parameter—a “builder” object to aid in registering services—and evaluates the block as if it were passed to <code>Registry#define</code> (see below).<br />
|
278
|
-
</p>
|
276
|
+
<p>This block accepts a single parameter—a “builder” object to aid in registering services—and evaluates the block as if it were passed to <code>Registry#define</code> (see below).</p>
|
279
277
|
</div>
|
280
278
|
|
281
279
|
|
@@ -290,14 +288,15 @@
|
|
290
288
|
<div class="section">
|
291
289
|
<p>Registering services with a Needle registry is very straightforward. The simplest way to do it is:</p>
|
292
290
|
|
291
|
+
|
293
292
|
<pre>
|
294
293
|
registry.register( :foo ) { Bar.new }
|
295
294
|
</pre>
|
296
|
-
|
297
295
|
<p>The above will register a new service with the registry, naming it <code>:foo</code>. When <code>:foo</code> is requested from the registry, a new instance of <code>Bar</code> will be instantiated and returned.</p>
|
298
296
|
|
299
297
|
<p>You get services from the registry in either of two ways:</p>
|
300
298
|
|
299
|
+
|
301
300
|
<pre>
|
302
301
|
# Treating the registry as a Hash
|
303
302
|
svc = registry[:foo]
|
@@ -305,13 +304,13 @@
|
|
305
304
|
# Treating the service as a property of the registry
|
306
305
|
svc = registry.foo
|
307
306
|
</pre>
|
308
|
-
|
309
307
|
<h3>Convenience Methods</h3>
|
310
308
|
|
311
309
|
<p>Because you will often need to register many services with a registry at once, two convenience methods have been provided to make this use case lean and mean.</p>
|
312
310
|
|
313
311
|
<p>The first is <code>define</code>. Just pass a block to define that accepts one parameter. This parameter will be a “builder” object that allows you to define services just by sending them as messages to the builder:</p>
|
314
312
|
|
313
|
+
|
315
314
|
<pre>
|
316
315
|
registry.define do |b|
|
317
316
|
b.foo { Bar.new }
|
@@ -319,9 +318,9 @@
|
|
319
318
|
...
|
320
319
|
end
|
321
320
|
</pre>
|
322
|
-
|
323
321
|
<p>Alternative, you can call <code>define!</code>, passing a block that accepts no parameters. This block will be evaluated in the “builder” object’s context, with any unrecognized method call being interpreted as a new service registration of that name:</p>
|
324
322
|
|
323
|
+
|
325
324
|
<pre>
|
326
325
|
registry.define! do
|
327
326
|
foo { Bar.new }
|
@@ -329,22 +328,53 @@
|
|
329
328
|
...
|
330
329
|
end
|
331
330
|
</pre>
|
332
|
-
|
333
331
|
<p>Both of the above will register two new services with the registry, <code>:foo</code> and <code>:bar</code>.</p>
|
334
332
|
|
335
333
|
<h3>Default Lifecycle</h3>
|
336
334
|
|
337
335
|
<p>By default, a service is only instantiated once per registry. This means that (using the above example) if the service <code>:foo</code> were queried twice, the registry would return the same object for both queries:</p>
|
338
336
|
|
337
|
+
|
339
338
|
<pre>
|
340
339
|
svc1 = registry.foo
|
341
340
|
svc2 = registry.foo
|
342
341
|
|
343
342
|
p svc1.object_id == svc2.object_id #=> true
|
344
343
|
</pre>
|
344
|
+
<p>You can change this behavior, with <em>service models</em>. See the chapter on Service Models for more information.</p>
|
345
|
+
|
346
|
+
<h3>Parameterized Services</h3>
|
347
|
+
|
348
|
+
<p>Needle also supports <em>parameterized services</em>. These are services that, when requested, require contextual information to be passed as a parameter so that the service can be correctly initialized.</p>
|
349
|
+
|
350
|
+
<p>Consider the following example, in which some hypothetical <code>Printer</code> class represents any of a number of printers, based on a parameter given to its constructor.</p>
|
351
|
+
|
352
|
+
|
353
|
+
<pre>
|
354
|
+
registry.register( :printer, :model => :multiton ) do |c,p,name|
|
355
|
+
Printer.new( c.log_for( p ), name )
|
356
|
+
end
|
357
|
+
|
358
|
+
mono = registry.printer( :monochrome )
|
359
|
+
color = registry.printer( :color )
|
360
|
+
</pre>
|
361
|
+
<p>There are a few things to note about the above example:</p>
|
362
|
+
<ol>
|
363
|
+
<li>The <code>:multiton</code> model is explicitly requested. This is necessary because the default service model (<code>:singleton</code>) does not allow parameterized services. Most of the time, you’ll use the multiton service model with parameterized services, but you don’t have to. You could also use any of the prototype models as well.
|
364
|
+
</li>
|
365
|
+
<li>The constructor block for the <code>:printer</code> service takes three parameters, <code>c</code>, <code>p</code>, and <code>name</code>. The first two parameters, <code>c</code> and <code>p</code>, represent the container and the service point, respectively. Any parameters after those two are the contextual parameters given when the service is requested. In this case, there is only one contextual parameter: the name of the printer.
|
366
|
+
</li>
|
367
|
+
<li>Notice the first parameter to the Printer constructor: <code>c.log_for(p)</code>. This is itself invoking a parameterized service, named <code>:log_for</code>, and passing <code>p</code> as the contextual information. This will return a new logger handle for the service point <code>p</code> (i.e., the current service point).
|
368
|
+
</li>
|
369
|
+
<li>See how the printer service is requested on the last two lines. In this case, the <code>#printer</code> message is sent to the registry with a single parameter. You can also request the service in two other ways:</li>
|
370
|
+
</ol>
|
345
371
|
|
346
|
-
|
347
|
-
|
372
|
+
|
373
|
+
<pre>
|
374
|
+
dot_matrix = registry[ :printer, :dot_matrix ]
|
375
|
+
ink_jet = registry.get( :printer, :ink_jet )
|
376
|
+
</pre>
|
377
|
+
<p>Choose the style that works best for you.</p>
|
348
378
|
</div>
|
349
379
|
|
350
380
|
|
@@ -358,7 +388,6 @@
|
|
358
388
|
|
359
389
|
<div class="section">
|
360
390
|
<p>Namespaces allow you to organize your services. The feature has many different applications, including:</p>
|
361
|
-
|
362
391
|
<ol>
|
363
392
|
<li>Third-parties may distribute Needle-enabled libraries without worrying about their choice of service names conflicting with the service names of their clients.</li>
|
364
393
|
<li>Developers may organize complex applications into modules, and the service definitions may be stored in the registry to reflect that organization.</li>
|
@@ -367,53 +396,53 @@
|
|
367
396
|
|
368
397
|
<p>Creating a namespace is as easy as invoking the <code>#namespace</code> method of the registry (or of another namespace):</p>
|
369
398
|
|
399
|
+
|
370
400
|
<pre>
|
371
401
|
registry.namespace :stuff
|
372
402
|
</pre>
|
373
|
-
|
374
403
|
<p>This would create a new namespace in the registry called <code>:stuff</code>. The application may then proceed to register services inside that namespace:</p>
|
375
404
|
|
405
|
+
|
376
406
|
<pre>
|
377
407
|
registry.stuff.register( :foo ) { Bar.new }
|
378
408
|
...
|
379
409
|
svc = registry.stuff.foo
|
380
410
|
</pre>
|
381
|
-
|
382
411
|
<p>Here’s a tip: <em>namespaces are just a special kind of service.</em> This means that you can access namespaces in the same ways that you can access services:</p>
|
383
412
|
|
413
|
+
|
384
414
|
<pre>
|
385
415
|
svc = registry[:stuff][:foo]
|
386
416
|
</pre>
|
387
|
-
|
388
417
|
<h3>Convenience Methods</h3>
|
389
418
|
|
390
419
|
<p>Because it is often the case that you will be creating a namespace and then immediately registering services on it, you can pass a block to <code>namespace</code>. The block will receive a reference to the new namespace:</p>
|
391
420
|
|
421
|
+
|
392
422
|
<pre>
|
393
423
|
registry.namespace :stuff do |spc|
|
394
424
|
spc.register( :foo ) { Bar.new }
|
395
425
|
...
|
396
426
|
end
|
397
427
|
</pre>
|
398
|
-
|
399
428
|
<p>If you prefer the <code>define</code> approach to registering services, you may like <code>namespace_define</code>, which creates the new namespace and immediately calls <code>define</code> on it:</p>
|
400
429
|
|
430
|
+
|
401
431
|
<pre>
|
402
432
|
registry.namespace_define :stuff do |b|
|
403
433
|
b.foo { Bar.new }
|
404
434
|
...
|
405
435
|
end
|
406
436
|
</pre>
|
407
|
-
|
408
437
|
<p>And, to mirror the <code>namespace_define</code> method, there is also a <code>namespace_define!</code> method. This method creates a new namespace and then does a <code>define!</code> call on that namespace.</p>
|
409
438
|
|
439
|
+
|
410
440
|
<pre>
|
411
441
|
registry.namespace_define! :stuff do
|
412
442
|
foo { Bar.new }
|
413
443
|
...
|
414
444
|
end
|
415
445
|
</pre>
|
416
|
-
|
417
446
|
<p>The above code would create a new namespace called <code>:stuff</code> in the registry, and would then proceed to register a service called <code>:foo</code> in the new namespace.</p>
|
418
447
|
</div>
|
419
448
|
|
@@ -14,8 +14,8 @@
|
|
14
14
|
</div>
|
15
15
|
</td><td valign='middle' align='right'>
|
16
16
|
<div class="info">
|
17
|
-
Needle Version: <strong>1.
|
18
|
-
Manual Last Updated: <strong>2004-11-
|
17
|
+
Needle Version: <strong>1.2.0</strong><br />
|
18
|
+
Manual Last Updated: <strong>2004-11-18 15:36 GMT</strong>
|
19
19
|
</div>
|
20
20
|
</td></tr>
|
21
21
|
</table>
|
@@ -223,7 +223,6 @@
|
|
223
223
|
<p>The <em>service locator</em> design pattern can be considered a subset of dependency injection. Because it is simpler, it is as good of a place to start teaching DI as any.</p>
|
224
224
|
|
225
225
|
<p>To demonstrate both techniques, we’ll pretend we’re going to write an online forum application. To start, let’s come up with a rough design by cataloging the components we’ll need.</p>
|
226
|
-
|
227
226
|
<ul>
|
228
227
|
<li><code>Logger</code>. This will be used to write messages to a file.</li>
|
229
228
|
<li><code>Authenticator</code>. This will be used to validate whether a user is who they say they are.</li>
|
@@ -236,7 +235,6 @@
|
|
236
235
|
<p>(Of course, a <em>real</em> online forum application would be significantly more complex, but the above components will do for our purposes.)</p>
|
237
236
|
|
238
237
|
<p>The dependencies between these components are:</p>
|
239
|
-
|
240
238
|
<ul>
|
241
239
|
<li><code>Authenticator</code> <em>has</em> <code>Database</code> (for querying user authentication information) and <code>Logger</code></li>
|
242
240
|
<li><code>Database</code> <em>has</em> <code>Logger</code> (for indicating database accesses and query times)</li>
|
@@ -257,6 +255,7 @@
|
|
257
255
|
<div class="section">
|
258
256
|
<p>A conventional architecture will have each component instantiate its own dependencies. For example, the <code>Application</code> would do something like this:</p>
|
259
257
|
|
258
|
+
|
260
259
|
<pre>
|
261
260
|
class Application
|
262
261
|
def initialize
|
@@ -268,9 +267,9 @@
|
|
268
267
|
end
|
269
268
|
end
|
270
269
|
</pre>
|
271
|
-
|
272
270
|
<p>However, the above is already flawed, because the <code>Authenticator</code> and the <code>Session</code> both need access to the <code>Database</code>, so you really need to make sure you instantiate things in the right order and pass them as parameters to the constructor of each object that needs them, like so:</p>
|
273
271
|
|
272
|
+
|
274
273
|
<pre>
|
275
274
|
class Application
|
276
275
|
def initialize
|
@@ -282,9 +281,7 @@
|
|
282
281
|
end
|
283
282
|
end
|
284
283
|
</pre>
|
285
|
-
|
286
|
-
<p>The problem with this is that if you later decide that <code>View</code> needs to access the database, you need to rearrange the order of how things are instantiated in the <code>Application</code> constructor.<br />
|
287
|
-
</p>
|
284
|
+
<p>The problem with this is that if you later decide that <code>View</code> needs to access the database, you need to rearrange the order of how things are instantiated in the <code>Application</code> constructor.</p>
|
288
285
|
</div>
|
289
286
|
|
290
287
|
|
@@ -299,6 +296,7 @@
|
|
299
296
|
<div class="section">
|
300
297
|
<p>The <em>service locator</em> pattern makes things a <em>little</em> easier. Instead of instantiating everything in the constructor of the <code>Application</code>, you can create a factory method somewhere that returns the new <code>Application</code> instance. Then, inside of this factory method, you assign each new object to collection, and pass that collection to each constructor.</p>
|
301
298
|
|
299
|
+
|
302
300
|
<pre>
|
303
301
|
require 'needle'
|
304
302
|
|
@@ -334,7 +332,6 @@
|
|
334
332
|
|
335
333
|
...
|
336
334
|
</pre>
|
337
|
-
|
338
335
|
<p>This has the benefit of allowing each object to construct itself <em>� la carte</em> from the objects in the locator. Also, each object no longer cares what class implements each service—it only cares that each object implements the methods it will attempt to invoke on that object.</p>
|
339
336
|
|
340
337
|
<p>Also, because Needle defers the instantiation of each service until the service is actually requested, we can actually register each item with the locator in any arbitrary order. All that is happening is the block is associated with the symbol, so that when the service is requested, the corresponding block is invoked. What is more, by default each service is then cached, so that it is only instantiated once.</p>
|
@@ -343,6 +340,7 @@
|
|
343
340
|
|
344
341
|
<p>In the interest of brevity, the <code>create_application</code> could have been written like this, using a “builder” object (called <code>b</code> in the example below) to help register the services:</p>
|
345
342
|
|
343
|
+
|
346
344
|
<pre>
|
347
345
|
def create_application
|
348
346
|
locator = Needle::Registry.define do |b|
|