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 In most simple applications, a single container, like singleton or prototype one might be enough,
13 for using. Having a set of singletons constructed on behalf of a developer is convenient, yet there
14 are occurences a single container is not enough. For example when, a set of components rely on a
15 particular dependency, and more specifically, the dependency they rely upon, should not be shared
16 across reliant components. In another case, a set of components are stored in a database, in serialized
17 form, and they are used as dependencies for other components from application.
18 
19 In such cases use of a single container like singleton or prototype is not enough, since one set
20 of components should be created by using normal frameworks means, and another set needs to be
21 fetched from a database, or should not be shared to all dependent components. To solve this problem,
22 AEDI framework, allows container to be joined and used together, for creation of components. A
23 component in such a joint container, having a dependency on a prototype component, will have it’s
24 requirement fullfilled without a problem.
25 
26 Using as an example car simulation app, the company decided to add a set ”tires” to simulated
27 cars. For a particular car, each tire installed in it has same properties as other installed in same car.
28 Registering 4 times same tire is not cost effective. Instead of it, it is better to register component
29 into a prototype container and use component to supply 4 instances of a tire to a particular car.
30 Example below shows how two or more containers can be joined together, configured with components,
31 and used to instantiate required components.
32 ----------------
33 auto container = aggregate( // Create a joint container hosting other two containers
34         singleton(), "singleton", // Create singleton container, and store it in joint container by "singleton" identity
35         prototype(), "prototype" // Create prototype container, and store it in joint container by "prototype" identity
36     );
37 
38 with (container.configure("singleton")) { // Configure singleton container
39 
40     // ...
41 
42     register!Car
43         .autowire
44         .autowire!"color"
45         .set!"frontLeft"(lref!Tire)
46         .set!"frontRight"(lref!Tire)
47         .set!"backLeft"(lref!Tire)
48         .set!"backRight"(lref!Tire);
49 }
50 
51 with (container.configure("prototype")) { // Configure prototype container
52 
53     register!Tire // Registering Tire into "prototype" container used by aggregate container
54         .set!"size"(17)
55         .set!"pressure"(3.0)
56         .set!"vendor"("divine tire");
57 }
58 
59 //...
60 ----------------
61 To join one or more containers together, an aggregate container must be created that will host
62 both of them under the hood. Once aggregate container has all of joint containers registered in it,
63 the process of registering components takes place.
64 To register components in joint container, pass the identity of subcontainer (container in joint container)
65 to $(D_INLINECODE configure) as an argument, and register components for selected subcontainer.
66 The $(D_INLINECODE configure) function applied to joint container in $(D_INLINECODE with ()) statement
67 creates a configuration context, that stores the container where components are stored, and
68 container from which dependencies for those components should be fetched.
69 In case of joint container, and singleton subcontainer, singleton subcontainer acts as storage while
70 joint container is used as source for dependencies of registered components.
71 
72 In the result, the car simulator will be able to use a car, that has 4 different instances of a tire.
73 Output below shows the constructed car by joint containers.
74 -----------------
75 Uuh, what a nice car, Electric car with following specs:
76 Size:	Size(200, 150, 300)
77 Color:	Color(0, 0, 0)
78 Engine:	app.ElectricEngine
79 Tire front left:	Tire(17 inch, 3 atm, divine tire)	 located at memory 7FB560C31180
80 Tire front right:	Tire(17 inch, 3 atm, divine tire)	 located at memory 7FB560C311C0
81 Tire back left: 	Tire(17 inch, 3 atm, divine tire)	 located at memory 7FB560C31200
82 Tire back right:	Tire(17 inch, 3 atm, divine tire)	 located at memory 7FB560C31240
83 -----------------
84 
85 Notice that each tire is a different object, hence everything is working as we
86 expected!
87 
88 Try this example, modify it, play with it to understand how compositing can
89 help in designing your application.
90 
91 License:
92 	Boost Software License - Version 1.0 - August 17th, 2003
93 
94 	Permission is hereby granted, free of charge, to any person or organization
95 	obtaining a copy of the software and accompanying documentation covered by
96 	this license (the "Software") to use, reproduce, display, distribute,
97 	execute, and transmit the Software, and to prepare derivative works of the
98 	Software, and to permit third-parties to whom the Software is furnished to
99 	do so, all subject to the following:
100 
101 	The copyright notices in the Software and this entire statement, including
102 	the above license grant, this restriction and the following disclaimer,
103 	must be included in all copies of the Software, in whole or in part, and
104 	all derivative works of the Software, unless such copies or derivative
105 	works are solely in the form of machine-executable object code generated by
106 	a source language processor.
107 
108 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
109 	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
110 	FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
111 	SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
112 	FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
113 	ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
114 	DEALINGS IN THE SOFTWARE.
115 
116 Authors:
117 	aermicioi
118 **/
119 
120 module multiple_containers;
121 
122 import aermicioi.aedi;
123 import std.stdio;
124 
125 /**
126 A struct that should be managed by container.
127 **/
128 struct Color {
129     ubyte r;
130     ubyte g;
131     ubyte b;
132 }
133 
134 /**
135 Size of a car.
136 **/
137 struct Size {
138 
139     ulong width;
140     ulong height;
141     ulong length;
142 }
143 
144 /**
145 Interface for engines.
146 
147 An engine should implement it, in order to be installable in a car.
148 **/
149 interface Engine {
150 
151     public {
152 
153         void turnOn();
154         void run();
155         void turnOff();
156     }
157 }
158 
159 /**
160 A concrete implementation of Engine that uses gasoline for propelling.
161 **/
162 class GasolineEngine : Engine {
163 
164     public {
165 
166         void turnOn() {
167             writeln("pururukVrooomVrrr");
168 
169         }
170 
171         void run() {
172             writeln("vrooom");
173         }
174 
175         void turnOff() {
176             writeln("vrrrPrrft");
177         }
178     }
179 }
180 
181 /**
182 A concrete implementation of Engine that uses diesel for propelling.
183 **/
184 class DieselEngine : Engine {
185 
186     public {
187 
188         void turnOn() {
189             writeln("pururukVruumVrrr");
190 
191         }
192 
193         void run() {
194             writeln("vruum");
195         }
196 
197         void turnOff() {
198             writeln("vrrrPft");
199         }
200     }
201 }
202 
203 /**
204 A concrete implementation of Engine that uses electricity for propelling.
205 **/
206 class ElectricEngine : Engine {
207     public {
208 
209         void turnOn() {
210             writeln("pzt");
211 
212         }
213 
214         void run() {
215             writeln("vvvvvvvvv");
216         }
217 
218         void turnOff() {
219             writeln("tzp");
220         }
221     }
222 }
223 
224 /**
225 Tire, what it can represent else?
226 **/
227 class Tire {
228     private {
229         int size_;
230         float pressure_;
231         string vendor_;
232     }
233 
234     public @property {
235         Tire size(int size) @safe nothrow {
236         	this.size_ = size;
237 
238         	return this;
239         }
240 
241         int size() @safe nothrow {
242         	return this.size_;
243         }
244 
245         Tire pressure(float pressure) @safe nothrow {
246         	this.pressure_ = pressure;
247 
248         	return this;
249         }
250 
251         float pressure() @safe nothrow {
252         	return this.pressure_;
253         }
254 
255         Tire vendor(string vendor) @safe nothrow {
256         	this.vendor_ = vendor;
257 
258         	return this;
259         }
260 
261         string vendor() @safe nothrow {
262         	return this.vendor_;
263         }
264     }
265 
266     public override string toString() {
267         import std.algorithm;
268         import std.range;
269         import std.conv;
270         import std.utf;
271 
272         return only("Tire(", this.size.to!string, " inch, ", this.pressure.to!string, " atm, ", this.vendor, ")")
273             .joiner
274             .byChar
275             .array;
276     }
277 }
278 
279 /**
280 A class representing a car.
281 **/
282 class Car {
283 
284     private {
285         Color color_; // Car color
286         Size size_; // Car size
287         Engine engine_; // Car engine
288 
289         Tire frontLeft_;
290         Tire frontRight_;
291         Tire backLeft_;
292         Tire backRight_;
293     }
294 
295     public {
296 
297         /**
298         Constructor of car.
299 
300         Constructs a car with a set of sizes. A car cannot of course have
301         undefined sizes, so we should provide it during construction.
302 
303         Params:
304             size = size of a car.
305         **/
306         this(Size size, Engine engine) {
307             this.size_ = size;
308             this.engine = engine;
309         }
310 
311         @property {
312 
313             /**
314             Set color of car
315 
316             Set color of car. A car can live with undefined color (physics allow it).
317 
318             Params:
319             	color = color of a car.
320 
321             Returns:
322             	Car
323             **/
324             Car color(Color color) @safe nothrow {
325             	this.color_ = color;
326 
327             	return this;
328             }
329 
330             Color color() @safe nothrow {
331             	return this.color_;
332             }
333 
334             Size size() @safe nothrow {
335             	return this.size_;
336             }
337 
338             /**
339             Set engine used in car.
340 
341             Params:
342             	engine = engine used in car to propel it.
343 
344             Returns:
345             	Car
346             **/
347             Car engine(Engine engine) @safe nothrow {
348             	this.engine_ = engine;
349 
350             	return this;
351             }
352 
353             Engine engine() @safe nothrow {
354             	return this.engine_;
355             }
356 
357             Car frontLeft(Tire frontLeft) @safe nothrow {
358             	this.frontLeft_ = frontLeft;
359 
360             	return this;
361             }
362 
363             Tire frontLeft() @safe nothrow {
364             	return this.frontLeft_;
365             }
366 
367             Car frontRight(Tire frontRight) @safe nothrow {
368             	this.frontRight_ = frontRight;
369 
370             	return this;
371             }
372 
373             Tire frontRight() @safe nothrow {
374             	return this.frontRight_;
375             }
376 
377             Car backLeft(Tire backLeft) @safe nothrow {
378             	this.backLeft_ = backLeft;
379 
380             	return this;
381             }
382 
383             Tire backLeft() @safe nothrow {
384             	return this.backLeft_;
385             }
386 
387             Car backRight(Tire backRight) @safe nothrow {
388             	this.backRight_ = backRight;
389 
390             	return this;
391             }
392 
393             Tire backRight() @safe nothrow {
394             	return this.backRight_;
395             }
396 
397         }
398 
399         void start() {
400             engine.turnOn();
401         }
402 
403         void run() {
404             engine.run();
405         }
406 
407         void stop() {
408             engine.turnOff();
409         }
410     }
411 }
412 
413 /**
414 A manufacturer of cars.
415 **/
416 class CarManufacturer {
417 
418     public {
419         Car manufacture(Size size) {
420             return new Car(size, new DieselEngine()); // Manufacture a car.
421         }
422     }
423 }
424 
425 void drive(Car car, string name) {
426     writeln("Uuh, what a nice car, ", name," with following specs:");
427     writeln("Size:\t", car.size());
428     writeln("Color:\t", car.color());
429     writeln("Engine:\t", car.engine());
430     writeln("Tire front left:\t", car.frontLeft(), "\t located at memory ", cast(void*) car.frontLeft());
431     writeln("Tire front right:\t", car.frontRight(), "\t located at memory ", cast(void*) car.frontRight());
432     writeln("Tire back left: \t", car.backLeft(), "\t located at memory ", cast(void*) car.backLeft());
433     writeln("Tire back right:\t", car.backRight(), "\t located at memory ", cast(void*) car.backRight());
434 }
435 
436 void main() {
437     auto container = aggregate( // Create a joint container hosting other two containers
438         singleton(), "singleton", // Create singleton container, and store it in joint container by "singleton" identity
439         prototype(), "prototype" // Create prototype container, and store it in joint container by "prototype" identity
440     );
441 
442     scope(exit) container.terminate();
443 
444     with (container.configure("singleton")) { // Configure singleton container
445         register!Color; // Let's register a default implementation of Color
446 
447         register!Color("color.green") // Register "green" color into container.
448             .set!"r"(cast(ubyte) 0)
449             .set!"g"(cast(ubyte) 255)
450             .set!"b"(cast(ubyte) 0);
451 
452         register!Size // Let's register default implementation of a Size
453             .set!"width"(200UL)
454             .set!"height"(150UL)
455             .set!"length"(300UL);
456 
457         register!Size("size.sedan") // Register a size of a generic "sedan" into container
458             .set!"width"(200UL)
459             .set!"height"(150UL)
460             .set!"length"(500UL);
461 
462         register!(Engine, ElectricEngine);
463 
464         register!Car
465             .autowire
466             .autowire!"color"
467             .set!"frontLeft"(lref!Tire)
468             .set!"frontRight"(lref!Tire)
469             .set!"backLeft"(lref!Tire)
470             .set!"backRight"(lref!Tire);
471     }
472 
473     with (container.configure("prototype")) { // Configure prototype container
474 
475         register!Tire // Registering Tire into "prototype" container used by aggregate container
476             .set!"size"(17)
477             .set!"pressure"(3.0)
478             .set!"vendor"("divine tire");
479     }
480 
481     container.instantiate(); // Boot container (or prepare managed code/data).
482 
483     container.locate!Car.drive("Electric car");
484 }