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 }