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 Usage: 13 14 For an end consumer of Aedi library, it is rarely needed to extend it's logic 15 with something new. But for a developer that wants to integrate Aedi library 16 with his/her framework, sometimes it is required to extend library to integrate it 17 with rest of framework. Aedi was designed with purpose of extending it in two directions: 18 19 $(OL 20 $(LI Containers -> responsible for created components. ) 21 $(LI Component factories -> responsible for creating components ) 22 ) 23 24 A component factory, as name states is responsible for creation and assembly of components 25 registered in container, as well as destroying them. Containers are using them to instantiate components, 26 that are requested. In all tutorials up to this registering a component, was equivalent to creating a 27 factory for it, and registering it in container. Afterwards factory was returned to the client code, were it was 28 further configured, with construction and setter injections by code. 29 30 When the default features of framework are not enough, it is possible to extend it, in several 31 directions mentioned above. For defining a new way of creating components, a factory interface 32 in listing below is shown, which is minimal requirement for having the custom factories useable by 33 containers. A factory encapsulates the logic required as to create and destroy a component. 34 35 ------------------ 36 interface Factory(T) : LocatorAware!(), AllocatorAware!() { 37 38 public { 39 40 T factory(); 41 void destruct(ref T component); 42 43 @property { 44 TypeInfo type(); 45 } 46 } 47 } 48 ------------------ 49 50 For example purposes, during development of car simulation app, logging of car assembly 51 should be done. Example below shows a simple logging component factory that creates components 52 using zero argument constructor, and logs the construction using default logging facilities present in 53 D programming language. 54 55 ------------------ 56 class LoggingFactory(T) : Factory!T { 57 private { 58 Locator!() locator_; 59 RCIAllocator allocator_; 60 } 61 62 public { 63 @property { 64 typeof(this) allocator(RCIAllocator allocator) @safe nothrow pure { 65 this.allocator_ = allocator; 66 67 return this; 68 } 69 70 RCIAllocator allocator() @safe nothrow pure { 71 return this.allocator_; 72 } 73 } 74 75 T factory() { 76 import std.experimental.logger; 77 78 info("Creating component"); 79 static if (is(T : Object)) { 80 auto t = this.allocator.make!T; 81 } else { 82 auto t = T(); 83 } 84 info("Ended component creation"); 85 86 return t; 87 } 88 89 void destruct(ref T component) { 90 import std.experimental.logger; 91 92 info("Destroying component"); 93 static if (is(T : Object)) { 94 this.allocator.dispose(component); 95 info("Done destroying component"); 96 } else { 97 info("Value component nothing to destroy"); 98 } 99 } 100 101 TypeInfo type() { 102 return typeid(T); 103 } 104 105 LoggingFactory!T locator(Locator!() locator) @safe nothrow { 106 this.locator_ = locator; 107 108 return this; 109 } 110 } 111 } 112 ------------------ 113 114 A design decision in framework was made, that all containers must store only objects rooted 115 in Object class. Since the language in which framework is developed supports not only object, but 116 structs, unions and a multitude of value based types, those types should be wrapped into an object 117 to be stored into containers. By convention, it is assumed that value based data is wrapped in an 118 implementation of $(D_INLINECODE Wrapper) interface which roots into Object class. Knowing this, factories that 119 attempt to supply value based dependencies to components, fetch the wrapped components from 120 container, extracts the component from wrapper and pass it to the component. 121 122 From the constraint that containers apply on accepted types (rooted in Object), the same requirements are propagated 123 to component factories which are stored in containers. To leverage this problem, framework 124 provides a decorating factory, that wraps up another factory, and exposes an compatible interface 125 for containers. It will automatically wrap any component that is not rooted in Object class into a 126 $(D_INLINECODE Wrapper) implementation and give it further to container. 127 128 For aesthetic purposes it is recommended any creation of a factory to be wrapped in a function, 129 which itself can serve as a facade to implementation, and be integrated seamlessly in code api. 130 Example below shows an example of such a wrapper, that handles creation of a component factory and 131 wrapping it in a compatible interface for containers. 132 133 ------------------ 134 auto registerLogged(Type)(Storage!(ObjectFactory, string) container, string identity) { 135 auto factory = new LoggingFactory!Type; 136 container.set(new WrappingFactory!(LoggingFactory!Type)(factory), identity); 137 138 return factory; 139 } 140 ------------------ 141 142 To test custom component factory example below shows how it can be used seamlessly and 143 unknowingly from the point of view of a user of library. 144 145 ------------------ 146 void main() { 147 148 SingletonContainer container = singleton(); 149 150 container.registerLogged!Tire("logging.tire"); 151 container.registerLogged!int("logging.int"); 152 153 container.instantiate(); 154 155 container.locate!Tire("logging.tire").writeln; 156 container.locate!int("logging.int").writeln; 157 } 158 ------------------ 159 160 Running the container we will get following result: 161 ------------------ 162 2017-03-04T00:34:33.401:app.d:factory:161 Creating our little component 163 2017-03-04T00:34:33.401:app.d:factory:167 Ended creation of component 164 2017-03-04T00:34:33.401:app.d:factory:161 Creating our little component 165 2017-03-04T00:34:33.401:app.d:factory:167 Ended creation of component 166 Tire(0 inch, nan atm, ) 167 0 168 ------------------ 169 170 As we can see our logging factory is doing designed job. 171 Try to implement your own factory, to understand how Aedi works behind the scenes. 172 173 License: 174 Boost Software License - Version 1.0 - August 17th, 2003 175 176 Permission is hereby granted, free of charge, to any person or organization 177 obtaining a copy of the software and accompanying documentation covered by 178 this license (the "Software") to use, reproduce, display, distribute, 179 execute, and transmit the Software, and to prepare derivative works of the 180 Software, and to permit third-parties to whom the Software is furnished to 181 do so, all subject to the following: 182 183 The copyright notices in the Software and this entire statement, including 184 the above license grant, this restriction and the following disclaimer, 185 must be included in all copies of the Software, in whole or in part, and 186 all derivative works of the Software, unless such copies or derivative 187 works are solely in the form of machine-executable object code generated by 188 a source language processor. 189 190 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 191 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 192 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 193 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 194 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 195 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 196 DEALINGS IN THE SOFTWARE. 197 198 Authors: 199 aermicioi 200 **/ 201 202 module extending_factory; 203 204 import aermicioi.aedi; 205 import std.stdio; 206 import std.experimental.allocator; 207 208 class LoggingFactory(T) : Factory!T { 209 private { 210 Locator!() locator_; 211 RCIAllocator allocator_; 212 } 213 214 public { 215 @property { 216 /** 217 Set allocator 218 219 Params: 220 allocator = allocator that is used to allocate the component 221 222 Returns: 223 typeof(this) 224 **/ 225 typeof(this) allocator(RCIAllocator allocator) @safe nothrow pure { 226 this.allocator_ = allocator; 227 228 return this; 229 } 230 231 /** 232 Get allocator 233 234 Returns: 235 RCIAllocator 236 **/ 237 inout(RCIAllocator) allocator() @safe nothrow pure inout { 238 return this.allocator_; 239 } 240 } 241 242 T factory() { 243 import std.experimental.logger; 244 245 info("Creating component"); 246 static if (is(T : Object)) { 247 auto t = this.allocator.make!T; 248 } else { 249 auto t = T(); 250 } 251 info("Ended component creation"); 252 253 return t; 254 } 255 256 void destruct(ref T component) { 257 import std.experimental.logger; 258 259 info("Destroying component"); 260 static if (is(T : Object)) { 261 this.allocator.dispose(component); 262 info("Done destroying component"); 263 } else { 264 info("Value component nothing to destroy"); 265 } 266 } 267 268 TypeInfo type() const { 269 return typeid(T); 270 } 271 272 LoggingFactory!T locator(Locator!() locator) @safe nothrow { 273 this.locator_ = locator; 274 275 return this; 276 } 277 } 278 } 279 280 class Tire { 281 private { 282 int size_; 283 float pressure_; 284 string vendor_; 285 } 286 287 public @property { 288 Tire size(int size) @safe nothrow { 289 this.size_ = size; 290 291 return this; 292 } 293 294 int size() @safe nothrow { 295 return this.size_; 296 } 297 298 Tire pressure(float pressure) @safe nothrow { 299 this.pressure_ = pressure; 300 301 return this; 302 } 303 304 float pressure() @safe nothrow { 305 return this.pressure_; 306 } 307 308 Tire vendor(string vendor) @safe nothrow { 309 this.vendor_ = vendor; 310 311 return this; 312 } 313 314 string vendor() @safe nothrow { 315 return this.vendor_; 316 } 317 } 318 319 public override string toString() { 320 import std.algorithm; 321 import std.range; 322 import std.conv; 323 import std.utf; 324 325 return only("Tire(", this.size.to!string, " inch, ", this.pressure.to!string, " atm, ", this.vendor, ")") 326 .joiner 327 .byChar 328 .array; 329 } 330 } 331 332 auto registerLogged(Type)(Storage!(ObjectFactory, string) container, string identity) { 333 auto factory = new LoggingFactory!Type; 334 container.set(new WrappingFactory!(LoggingFactory!Type)(factory), identity); 335 336 return factory; 337 } 338 339 void main() { 340 341 SingletonContainer container = singleton(); 342 scope (exit) container.terminate(); 343 344 container.registerLogged!Tire("logging.tire"); 345 container.registerLogged!int("logging.int"); 346 347 container.instantiate(); 348 349 container.locate!Tire("logging.tire").writeln; 350 container.locate!int("logging.int").writeln; 351 }