1 /**
2 Aedi, a dependency injection library.
3 
4 Aedi is a dependency injection library. It does provide a set of containers that do
5 IoC, and an interface to configure application components (structs, objects, etc.)
6 
7 Aim:
8 
9 The aim of library is to provide a dependency injection solution that is
10 feature rich, easy to use, easy to learn, and easy to extend up to your needs.
11 
12 Besides $(D_INLINECODE set) , $(D_INLINECODE construct) , methods used in all above examples, framework offers several
13 other configuration methods, that allow greater manipulation, or less verbosity. Additional configu-
14 ration primitives are listed below:
15 
16 $(OL
17     $(LI $(D_INLINECODE autowire) )
18     $(LI $(D_INLINECODE factoryMethod) )
19     $(LI $(D_INLINECODE callback) )
20     $(LI $(D_INLINECODE value))
21     $(LI $(D_INLINECODE destructor))
22 )
23 
24 Lets start with first one from list, which is $(D_INLINECODE autowire) method.
25 From it's name we can deduce that it automates wiring of components somehow.
26 But how does it do this? Let's register a car using old $(D_INLINECODE set) and $(D_INLINECODE construct) methods:
27 ----------------
28     register!Car("sedan.engine.default")
29         .construct(lref!Size, lref!Engine)
30         .set!"color"(lref!Color);
31 ----------------
32 
33 Autowire:
34 
35 Even with more structured instantiation code present using framework, sometimes, even this
36 code is too verbose. Some components for example have a set of dependencies defined by some
37 interface, and having for each dependency to reference it using $(D_INLINECODE lref!Type) is quite cumbersome,
38 and painfull.
39 
40 And here comes into play $(D_INLINECODE autowire) configuration method, which handles referencing of
41 component by type on behalf of developer that creates the configuration, like in example below:
42 
43 -----------------
44 register!Car("sedan.engine.default") // Register a car with a default engine.
45     .autowire() // Construct Car using default implementation of a size, and engine.
46     .autowire!"color"(); // Inject default implementation of a Color.
47 -----------------
48 
49 In current implementation of autowired due to limitations of current compiler’s compile time
50 introspection, $(D_INLINECODE autowire) cannot work well with overloaded methods or constructors. In such cases,
51 $(D_INLINECODE autowire) cannot know which version of method/constructor to overload, and will default to the
52 first one from the list of overloads.
53 
54 FactoryMethod:
55 
56 Frequently in applications that use third party code, components from that third party code,
57 are instantiated using factories or other entities from that third party code, and the client code of
58 those libraries simply are not allowed to manually create those types. To aleviate this problem, when
59 such a component is registered in a container, framework allows such components to be instantiated
60 by using third party code responsible for this task.
61 
62 Supposedly, the car simulation application introduced previously, is upgraded with a car man-
63 ufacturing simulation, and new cars that are needed to be registered as components into container
64 are required to be instantiated using car manufacturing component. $(D_INLINECODE factoryMethod) method
65 instructs framework to use a third party component to instantiate registered component, using static
66 method of a factory, or by using an instance of a factory. In example usage of $(D_INLINECODE factoryMethod)
67 is shown:
68 
69 -----------------
70 register!Car("car_manufacturer.product.sedan") // Register a car with gasoline engine
71     .factoryMethod!(CarManufacturer, "manufacture")(lref!CarManufacturer, "size.sedan".lref);
72 -----------------
73 
74 Callback:
75 While just by configuring a component with simple values or references, in some cases during
76 construction of a component some custom logic must be run, that is or not related to instantiation
77 process. $(D_INLINECODE callback) configuration method can be used to pass a function or a delegate that
78 constructs or configures the component, according to some custom logic, not expressable by already
79 available configuration primitives. Example below shows how a callback can be used as a constructor or
80 as a configuration primitive.
81 
82 ------------------
83     register!Car("sedan.engine.electric") // Register a car with electric engine
84         .callback(
85             delegate (Locator!() loc) { // Let's construct our car using some custom logic that isn't possible to express with a simple construct method call.
86                 Car car = new Car(loc.locate!Size, new ElectricEngine());
87 
88                 return car;
89             }
90         ).callback( // Let's configure newly built car with a custom color.
91             delegate (Locator!() loc, Car car) {
92                 car.color = loc.locate!Color;
93             }
94         );
95 -------------------
96 Though it's quite a simple primitive, it is as well powerful one. It is possible to define your own
97 custom configuration primitives just by using under the hood the $(D_INLINECODE callback) primitive.
98 
99 Value:
100 The value primitive instructs container to use as basis an already constructed component. The component itself
101 can be further configured using primitives specified above. The only specific case that should be taken into accout
102 is the fact that using same value that is a reference type (a pointer, or class) in multiple components, will result into
103 having same component with multiple identities in container. The container itself won't be aware of the relationship, as in case
104 with aliasing. It is recommended that $(D_INLINECODE value) to be used only once per reference value.
105 
106 -------------------
107 register!GasolineEngine
108     .value(new GasolineEngine(15)); // Register a gasoline engine. Note: this engine is not the same gasoline engine from default implementation.
109 -------------------
110 
111 Destructor:
112 When container is shut down, the default behavior is to destroy all constructed components. The default destruction logic is to run destructor and
113 deallocate the component. Sometimes, the default behavior is not desired, or additional logic should be run. In such cases $(D_INLINECODE destructor)
114 primitive can be used to either pass a delegate, or factory method to be used to destroy the component.
115 
116 -------------------
117 register!Car("car_manufacturer.product.sedan") // Register a car with gasoline engine
118     .factoryMethod!(CarManufacturer, "manufacture")(lref!CarManufacturer, "size.sedan".lref)
119     .destructor((RCIAllocator alloc, ref Car car) {
120         write("Crushing the car. ");
121         alloc.dispose(car);
122         writeln("Done");
123     });
124 -------------------
125 
126 Those additional primitives are quite handy to use, with libraries that are usign factories,
127 dependencies by interface, or when some custom behavior has to be run during construction of a
128 component. Output below is the result of using those primitives, to run.
129 
130 -------------------
131 Uuh, what a nice car, Gasoline car with following specs:
132 Size:	Size(200, 150, 300)
133 Color:	Color(0, 0, 0)
134 Engine:	app.GasolineEngine
135 Let`s turn it on
136 pururukVrooomVrrr
137 What a nice sound! We should make a test drive!
138 vrooom
139 Umm the test drive was awesome, let`s get home and turn it off.
140 vrooom
141 vrrrPrrft
142 
143 Uuh, what a nice car, Manufactured car with following specs:
144 Size:	Size(200, 150, 500)
145 Color:	Color(0, 0, 0)
146 Engine:	app.DieselEngine
147 Let`s turn it on
148 pururukVruumVrrr
149 What a nice sound! We should make a test drive!
150 vruum
151 Umm the test drive was awesome, let`s get home and turn it off.
152 vruum
153 vrrrPft
154 
155 Uuh, what a nice car, Electric car with following specs:
156 Size:	Size(200, 150, 300)
157 Color:	Color(0, 0, 0)
158 Engine:	app.ElectricEngine
159 Let`s turn it on
160 pzt
161 What a nice sound! We should make a test drive!
162 vvvvvvvvv
163 Umm the test drive was awesome, let`s get home and turn it off.
164 vvvvvvvvv
165 tzp
166 -------------------
167 
168 If you are puzzled from where is this output, take a look at the source code of
169 this example. Modify it, play with it to understand these configuration primitives.
170 
171 License:
172 	Boost Software License - Version 1.0 - August 17th, 2003
173 
174 	Permission is hereby granted, free of charge, to any person or organization
175 	obtaining a copy of the software and accompanying documentation covered by
176 	this license (the "Software") to use, reproduce, display, distribute,
177 	execute, and transmit the Software, and to prepare derivative works of the
178 	Software, and to permit third-parties to whom the Software is furnished to
179 	do so, all subject to the following:
180 
181 	The copyright notices in the Software and this entire statement, including
182 	the above license grant, this restriction and the following disclaimer,
183 	must be included in all copies of the Software, in whole or in part, and
184 	all derivative works of the Software, unless such copies or derivative
185 	works are solely in the form of machine-executable object code generated by
186 	a source language processor.
187 
188 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
189 	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
190 	FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
191 	SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
192 	FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
193 	ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
194 	DEALINGS IN THE SOFTWARE.
195 
196 Authors:
197 	aermicioi
198 **/
199 
200 module configuration_primitives;
201 
202 import aermicioi.aedi;
203 import std.experimental.allocator;
204 import std.stdio;
205 
206 /**
207 A struct that should be managed by container.
208 **/
209 struct Color {
210     ubyte r;
211     ubyte g;
212     ubyte b;
213 }
214 
215 /**
216 Size of a car.
217 **/
218 struct Size {
219 
220     ulong width;
221     ulong height;
222     ulong length;
223 }
224 
225 /**
226 Interface for engines.
227 
228 An engine should implement it, in order to be installable in a car.
229 **/
230 interface Engine {
231 
232     public {
233 
234         void turnOn();
235         void run();
236         void turnOff();
237     }
238 }
239 
240 /**
241 A concrete implementation of Engine that uses gasoline for propelling.
242 **/
243 class GasolineEngine : Engine {
244 
245     private double consume;
246     public {
247         this(double consume) {
248             this.consume = consume;
249         }
250 
251         void turnOn() {
252             writeln("pururukVrooomVrrr");
253 
254         }
255 
256         void run() {
257             writeln("munch ", consume, " of litres vroom");
258         }
259 
260         void turnOff() {
261             writeln("vrrrPrrft");
262         }
263     }
264 }
265 
266 /**
267 A concrete implementation of Engine that uses diesel for propelling.
268 **/
269 class DieselEngine : Engine {
270 
271     public {
272 
273         void turnOn() {
274             writeln("pururukVruumVrrr");
275 
276         }
277 
278         void run() {
279             writeln("vruum");
280         }
281 
282         void turnOff() {
283             writeln("vrrrPft");
284         }
285     }
286 }
287 
288 /**
289 A concrete implementation of Engine that uses electricity for propelling.
290 **/
291 class ElectricEngine : Engine {
292     public {
293 
294         void turnOn() {
295             writeln("pzt");
296 
297         }
298 
299         void run() {
300             writeln("vvvvvvvvv");
301         }
302 
303         void turnOff() {
304             writeln("tzp");
305         }
306     }
307 }
308 
309 /**
310 A class representing a car.
311 **/
312 class Car {
313 
314     private {
315         Color color_; // Car color
316         Size size_; // Car size
317         Engine engine_; // Car engine
318     }
319 
320     public {
321 
322         /**
323         Constructor of car.
324 
325         Constructs a car with a set of sizes. A car cannot of course have
326         undefined sizes, so we should provide it during construction.
327 
328         Params:
329             size = size of a car.
330         **/
331         this(Size size, Engine engine) {
332             this.size_ = size;
333             this.engine = engine;
334         }
335 
336         @property {
337 
338             /**
339             Set color of car
340 
341             Set color of car. A car can live with undefined color (physics allow it).
342 
343             Params:
344             	color = color of a car.
345 
346             Returns:
347             	Car
348             **/
349             Car color(Color color) @safe nothrow {
350             	this.color_ = color;
351 
352             	return this;
353             }
354 
355             Color color() @safe nothrow {
356             	return this.color_;
357             }
358 
359             Size size() @safe nothrow {
360             	return this.size_;
361             }
362 
363             /**
364             Set engine used in car.
365 
366             Params:
367             	engine = engine used in car to propel it.
368 
369             Returns:
370             	Car
371             **/
372             Car engine(Engine engine) @safe nothrow {
373             	this.engine_ = engine;
374 
375             	return this;
376             }
377 
378             Engine engine() @safe nothrow {
379             	return this.engine_;
380             }
381         }
382 
383         void start() {
384             engine.turnOn();
385         }
386 
387         void run() {
388             engine.run();
389         }
390 
391         void stop() {
392             engine.turnOff();
393         }
394     }
395 }
396 
397 /**
398 A manufacturer of cars.
399 **/
400 class CarManufacturer {
401 
402     public {
403         Car manufacture(Size size) {
404             return new Car(size, new DieselEngine()); // Manufacture a car.
405         }
406     }
407 }
408 
409 void drive(Car car, string name) {
410     writeln("Uuh, what a nice car, ", name," with following specs:");
411     writeln("Size:\t", car.size());
412     writeln("Color:\t", car.color());
413     writeln("Engine:\t", car.engine());
414 
415     writeln("Let's turn it on");
416     car.start();
417 
418     writeln("What a nice sound! We should make a test drive!");
419     car.run();
420 
421     writeln("Umm the test drive was awesome, let's get home and turn it off.");
422     car.run();
423     car.stop();
424 }
425 
426 void main() {
427     SingletonContainer container = singleton(); // Creating container that will manage a color
428     scope(exit) container.terminate();
429 
430     with (container.configure) {
431         register!Color; // Let's register a default implementation of Color
432 
433         register!Color("color.green") // Register "green" color into container.
434             .set!"r"(cast(ubyte) 0)
435             .set!"g"(cast(ubyte) 255)
436             .set!"b"(cast(ubyte) 0);
437 
438         register!Size // Let's register default implementation of a Size
439             .set!"width"(200UL)
440             .set!"height"(150UL)
441             .set!"length"(300UL);
442 
443         register!Size("size.sedan") // Register a size of a generic "sedan" into container
444             .set!"width"(200UL)
445             .set!"height"(150UL)
446             .set!"length"(500UL);
447 
448         register!(Engine, GasolineEngine)
449             .construct(10.0); // Register a gasoline engine as default implementation of an engine
450         register!GasolineEngine
451             .value(new GasolineEngine(15)); // Register a gasoline engine. Note: this engine is not the same gasoline engine from default implementation.
452         register!DieselEngine; // Register a diesel engine
453 
454         register!CarManufacturer; // Let's register a car manufacturer in our container.
455 
456         register!Car("sedan.engine.default") // Register a car with a default engine.
457             .autowire() // Construct Car using default implementation of a size, and engine.
458             .autowire!"color"(); // Inject default implementation of a Color.
459 
460         register!Car("car_manufacturer.product.sedan") // Register a car with gasoline engine
461             .factoryMethod!(CarManufacturer, "manufacture")(lref!CarManufacturer, "size.sedan".lref)
462             .destructor((RCIAllocator alloc, ref Car car) {
463                 write("Crushing the car with ", typeid(car.engine), " engine: ");
464                 alloc.dispose(car);
465                 writeln("Done");
466             });
467 
468         register!Car("sedan.engine.electric") // Register a car with electric engine
469             .callback(
470                 delegate (RCIAllocator allocator, Locator!() loc) { // Let's construct our car using some custom logic that isn't possible to express with a simple construct method call.
471                     Car car = allocator.make!Car(loc.locate!Size, new ElectricEngine());
472 
473                     return car;
474                 }
475             ).callback( // Let's configure newly built car with a custom color.
476                 delegate (Locator!() loc, Car car) {
477                     car.color = loc.locate!Color;
478                 }
479             );
480     }
481 
482     container.instantiate(); // Boot container (or prepare managed code/data).
483 
484     container.locate!Car("sedan.engine.default").drive("Gasoline car");
485     container.locate!Car("car_manufacturer.product.sedan").drive("Manufactured car");
486     container.locate!Car("sedan.engine.electric").drive("Electric car");
487 }