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 The interface presented in previous tutorial is a minimalistic interface, and does not provide any ability to 13 customize the component that is created. It is practically a black box, that is convenient to use by 14 containers, and not by client code that needs to configure components registered in container. 15 16 To support better configuration, without loosing black boxiness of presented $(D_INLINECODE Factory) interface, 17 the process of creating and configuring a component was split into three distinct steps below: 18 $(OL 19 $(LI Allocation and construction of component ) 20 $(LI Configuration of component ) 21 $(LI Destruct and deallocation of component) 22 ) 23 24 The following steps are formalized into a set of interfaces shown below, where each step 25 itself is encapsulated into objects implementing presented interfaces. 26 27 -------------------- 28 interface PropertyConfigurer(T) : LocatorAware!() { 29 30 public { 31 32 void configure(ref T object); 33 } 34 } 35 36 interface InstanceFactory(T) : LocatorAware!(), AllocatorAware!() { 37 38 public { 39 40 T factory(); 41 } 42 } 43 44 interface InstanceDestructor(T) : LocatorAware!(), AllocatorAware!() { 45 46 public { 47 48 void destruct(ref T destructable); 49 } 50 } 51 52 interface GenericFactory(T) : Factory!T, InstanceFactoryAware!T, PropertyConfigurersAware!T { 53 54 public { 55 56 @property { 57 58 alias locator = Factory!T.locator; 59 60 Locator!() locator(); 61 } 62 } 63 } 64 65 interface InstanceFactoryAware(T) { 66 public { 67 68 @property { 69 InstanceFactoryAware!T setInstanceFactory(InstanceFactory!T factory); 70 } 71 } 72 } 73 74 interface InstanceDestructorAware(T) { 75 public { 76 77 @property { 78 InstanceDestructorAware!T setInstanceDestructor(InstanceDestructor!T destructor); 79 } 80 } 81 } 82 83 interface PropertyConfigurersAware(T) { 84 public { 85 PropertyConfigurersAware!T addPropertyConfigurer(PropertyConfigurer!T configurer); 86 } 87 } 88 -------------------- 89 90 The methods used to configure components in examples up to this point are actually wrappers 91 over building blocks that implement interfaces presented above. $(D_INLINECODE register) method is a 92 wrapper over an implementation of $(D_INLINECODE GenericFactory) interface. 93 94 When instantiation or configuration of a component is not possible to implement in terms of 95 existing tools and interfaces provided by the framework, it is recommended to implement one of 96 building block interface, depending on what stage the problem resides. For car simulation app, with- 97 out reason the company decided that tires, should be instantiated using their own implementation of 98 instance building block. Same with inflating a tire they've decided to use their own implementation, 99 along with tire recycling, making a closed production cycle. 100 101 Next example shows a custom implementation of tire manufacturing process. By implementing 102 $(D_INLINECODE InstanceFactory) interface, the building block can be used along the rest of components already 103 present in framework, such as $(D_INLINECODE GenericFactory). The only hinder is that what remains is the ugliness, and 104 verbosity, due to need to manually instantiate the building block, and pass to generic factory. Instead 105 of manually doing it, wrapping instantiator into a function will lessen the verbosity (check $(D_INLINECODE makeTire) 106 definition and usage), and increase the readability of configuration code, thanks to UFCS feature of 107 D programming language. 108 109 -------------------- 110 auto makeTire(T : GenericFactory!Z, Z : Tire)(auto ref T factory, int size) { 111 factory.setInstanceFactory(new TireConstructorInstanceFactory(size)); 112 113 return factory; 114 } 115 116 class TireConstructorInstanceFactory : InstanceFactory!Tire { 117 118 private { 119 int size; 120 121 Locator!(Object, string) locator_; 122 RCIAllocator allocator_; 123 } 124 125 public { 126 this(int size) { 127 this.size = size; 128 } 129 130 @property { 131 TireConstructorInstanceFactory locator(Locator!(Object, string) locator) { 132 this.locator_ = locator; 133 134 return this; 135 } 136 137 TireConstructorInstanceFactory allocator(RCIAllocator allocator) pure nothrow @safe { 138 this.allocator_ = allocator; 139 140 return this; 141 } 142 } 143 144 Tire factory() { 145 Tire tire; 146 147 import std.stdio; 148 149 write("Creating a tire of ", size, " inches: "); 150 tire = this.allocator_.make!Tire; 151 tire.size = size; 152 writeln("\t[..OK..]"); 153 154 return tire; 155 } 156 } 157 } 158 -------------------- 159 160 The code for tire inflator configurer can be seen in examples from source code. The difference 161 from $(D_INLINECODE TireConstructorInstanceFactory) is that implementation should extend $(D_INLINECODE PropertyConfigurer). 162 Same can be said about tire destructor, it implements $(D_INLINECODE InstanceDestructor) to provide 163 destruction mechanisms for generic factory. 164 165 Once the custom implementation and their wrapper are implemented, it is possible to use them 166 as any other built-in building blocks from framework. Next example shows how implemented building 167 blocks can be used along with other configuration primitives. 168 -------------------- 169 void main() { 170 171 SingletonContainer container = new SingletonContainer; 172 scope (exit) container.terminate(); 173 174 with (container.configure) { 175 176 register!Tire("logging.tire") 177 .makeTire(17) 178 .inflateTire(3.0) 179 .set!"vendor"("hell tire") 180 .destroyTire; 181 } 182 183 container.instantiate(); 184 185 container.locate!Tire("logging.tire").writeln; 186 } 187 -------------------- 188 189 Running it, produces following result: 190 -------------------- 191 Creating a tire of 17 inches: [..OK..] 192 Inflating tire to 3 atm: [..OK..] 193 Tire(17 inch, 3 atm, hell tire) 194 Destroying tire: [..OK..] 195 -------------------- 196 197 Try to implement your own instance factory or property configurer, wrap them 198 in a function, and use in component configuration. Try to do this for $(D_INLINECODE Car) 199 component from previous examples. 200 201 License: 202 Boost Software License - Version 1.0 - August 17th, 2003 203 204 Permission is hereby granted, free of charge, to any person or organization 205 obtaining a copy of the software and accompanying documentation covered by 206 this license (the "Software") to use, reproduce, display, distribute, 207 execute, and transmit the Software, and to prepare derivative works of the 208 Software, and to permit third-parties to whom the Software is furnished to 209 do so, all subject to the following: 210 211 The copyright notices in the Software and this entire statement, including 212 the above license grant, this restriction and the following disclaimer, 213 must be included in all copies of the Software, in whole or in part, and 214 all derivative works of the Software, unless such copies or derivative 215 works are solely in the form of machine-executable object code generated by 216 a source language processor. 217 218 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 219 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 220 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 221 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 222 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 223 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 224 DEALINGS IN THE SOFTWARE. 225 226 Authors: 227 aermicioi 228 **/ 229 230 module extending_generic_factory; 231 232 import aermicioi.aedi; 233 import std.stdio; 234 import std.experimental.allocator; 235 236 class TireConstructorInstanceFactory : InstanceFactory!Tire { 237 238 private { 239 int size; 240 241 Locator!(Object, string) locator_; 242 RCIAllocator allocator_; 243 } 244 245 public { 246 this(int size) { 247 this.size = size; 248 } 249 250 @property { 251 TireConstructorInstanceFactory locator(Locator!(Object, string) locator) { 252 this.locator_ = locator; 253 254 return this; 255 } 256 257 TireConstructorInstanceFactory allocator(RCIAllocator allocator) pure nothrow @safe { 258 this.allocator_ = allocator; 259 260 return this; 261 } 262 } 263 264 Tire factory() @trusted { 265 Tire tire; 266 267 import std.stdio; 268 269 write("Creating a tire of ", size, " inches: "); 270 tire = this.allocator_.make!Tire; 271 tire.size = size; 272 writeln("\t[..OK..]"); 273 274 return tire; 275 } 276 } 277 } 278 279 class TireInflatorConfigurer : PropertyConfigurer!Tire { 280 281 private { 282 float atmospheres; 283 Locator!() locator_; 284 } 285 286 public { 287 288 this(float atmospheres = 3.0) { 289 this.atmospheres = atmospheres; 290 } 291 292 @property { 293 TireInflatorConfigurer locator(Locator!(Object, string) locator) { 294 this.locator_ = locator; 295 296 return this; 297 } 298 } 299 300 void configure(ref Tire tire) { 301 302 import std.stdio; 303 304 write("Inflating tire to ", atmospheres, " atm: "); 305 tire.pressure = atmospheres; 306 writeln("\t[..OK..]"); 307 } 308 } 309 } 310 311 class TireInstanceDestructor : InstanceDestructor!Tire { 312 mixin LocatorAwareMixin!TireInstanceDestructor; 313 mixin AllocatorAwareMixin!TireInstanceDestructor; 314 315 public { 316 void destruct(ref Tire destructable) @trusted { 317 write("Destroying tire: "); 318 this.allocator.dispose(destructable); 319 writeln("\t[..OK..]"); 320 } 321 } 322 } 323 324 auto makeTire(T : GenericFactory!Z, Z : Tire)(auto ref T factory, int size) { 325 factory.setInstanceFactory(new TireConstructorInstanceFactory(size)); 326 327 return factory; 328 } 329 330 auto inflateTire(T : GenericFactory!Z, Z : Tire)(auto ref T factory, float pressure) { 331 factory.addPropertyConfigurer(new TireInflatorConfigurer(pressure)); 332 333 return factory; 334 } 335 336 auto destroyTire(T : GenericFactory!Z, Z : Tire)(auto ref T factory) { 337 factory.setInstanceDestructor(new TireInstanceDestructor()); 338 339 return factory; 340 } 341 342 class Tire { 343 private { 344 int size_; 345 float pressure_; 346 string vendor_; 347 } 348 349 public @property { 350 Tire size(int size) @safe nothrow { 351 this.size_ = size; 352 353 return this; 354 } 355 356 int size() @safe nothrow { 357 return this.size_; 358 } 359 360 Tire pressure(float pressure) @safe nothrow { 361 this.pressure_ = pressure; 362 363 return this; 364 } 365 366 float pressure() @safe nothrow { 367 return this.pressure_; 368 } 369 370 Tire vendor(string vendor) @safe nothrow { 371 this.vendor_ = vendor; 372 373 return this; 374 } 375 376 string vendor() @safe nothrow { 377 return this.vendor_; 378 } 379 } 380 381 public override string toString() { 382 import std.algorithm; 383 import std.range; 384 import std.conv; 385 import std.utf; 386 387 return only("Tire(", this.size.to!string, " inch, ", this.pressure.to!string, " atm, ", this.vendor, ")") 388 .joiner 389 .byChar 390 .array; 391 } 392 } 393 394 void main() { 395 396 SingletonContainer container = new SingletonContainer; 397 scope (exit) container.terminate(); 398 399 with (container.configure) { 400 401 register!Tire("logging.tire") 402 .makeTire(17) 403 .inflateTire(3.0) 404 .set!"vendor"("hell tire") 405 .destroyTire; 406 } 407 408 container.instantiate(); 409 410 container.locate!Tire("logging.tire").writeln; 411 }