1 /** 2 * Модуль работы с компонентами и фабриками к ним 3 * 4 * Copyright: (c) 2015-2020, Milofon Project. 5 * License: Subject to the terms of the BSD 3-Clause License, as written in the included LICENSE.md file. 6 * Author: <m.galanin@milofon.pro> Maksim Galanin 7 * Date: 2020-04-22 8 */ 9 10 module dango.inject.factory; 11 12 private 13 { 14 import std.meta : Filter; 15 import std.format : fmt = format; 16 import std.traits : hasMember; 17 18 import bolts : isFunctionOver; 19 20 import dango.inject.container : DependencyContainer; 21 import dango.inject.provider : ClassProvider; 22 import dango.inject.injection : inject; 23 } 24 25 26 /** 27 * Интерфейс фабрики для создания компонентов системы 28 * Params: 29 * I - Конструируемый тип 30 * A - Типы аргументов 31 */ 32 interface ComponentFactory(C, A...) 33 if (is(C == class) || is(C == interface)) 34 { 35 alias ComponentType = C; 36 37 /** 38 * Создает компонент 39 */ 40 C createComponent(A args) @safe; 41 } 42 43 44 /** 45 * Метод позволяет создавать компоненты на основе анализа конструкторов 46 * Params: 47 * C = Тип создаваемого объекта 48 * args = Принимаемые аргументы 49 */ 50 C createComponentByCtor(C, A...)(A args) 51 if (is(C == class) || is(C == interface)) 52 { 53 enum hasValidCtor(alias ctor) = isFunctionOver!(ctor, args); 54 55 static if (hasMember!(C, "__ctor")) 56 { 57 alias pCtors = Filter!(hasValidCtor, 58 __traits(getOverloads, C, "__ctor")); 59 static if(pCtors.length) 60 return new C(args); 61 else static if (!A.length) 62 return new C(); 63 else 64 static assert(false, fmt!"Component %s is not create using argument types (%s)"( 65 C.stringof, A.stringof)); 66 } 67 else 68 return new C(); 69 } 70 71 72 version (unittest) 73 { 74 interface Server 75 { 76 string host() @safe; 77 ushort port() @safe; 78 } 79 80 class HTTPServer : Server 81 { 82 private 83 { 84 string _host; 85 ushort _port; 86 } 87 88 this() @safe {} 89 90 this(string host, ushort port) @safe 91 { 92 this._host = host; 93 this._port = port; 94 } 95 96 string host() @safe { return _host; } 97 ushort port() @safe { return _port; } 98 } 99 100 class ServerFactory : ComponentFactory!(Server, string, ushort) 101 { 102 Server createComponent(string host, ushort port) 103 { 104 return new HTTPServer(host, port); 105 } 106 } 107 } 108 109 @("Should work createComponentByCtor method") 110 @safe unittest 111 { 112 113 auto server = createComponentByCtor!(HTTPServer, string, ushort)("host", 3); 114 assert (server.host == "host"); 115 assert (server.port == 3); 116 117 server = createComponentByCtor!(HTTPServer)(); 118 assert (server.host == ""); 119 assert (server.port == 0); 120 } 121 122 123 /** 124 * Фабрика автосгенерирована на основе конструктора компонента 125 */ 126 template ComponentFactoryCtor(C, CT : C, A...) 127 if (is(C == class) || is(C == interface)) 128 { 129 class ComponentFactoryCtor : ComponentFactory!(C, DependencyContainer, A) 130 { 131 alias ConcreteType = CT; 132 133 /** 134 * See_Also: ComponentFactory.createComponent 135 */ 136 C createComponent(DependencyContainer container, A args) @safe 137 { 138 auto result = createComponentByCtor!(CT, A)(args); 139 inject!CT(container, result); 140 return result; 141 } 142 } 143 } 144 145 @("Should work ComponentFactoryCtor") 146 @safe unittest 147 { 148 auto cont = new DependencyContainer(); 149 auto factory = new ComponentFactoryCtor!(Server, HTTPServer, string, ushort)(); 150 auto server = factory.createComponent(cont, "host", 3); 151 assert (server.host == "host"); 152 assert (server.port == 3); 153 } 154 155 156 /** 157 * Оборачивание существующей фабрики в фабрику создающую 158 * объект с инъекцией зависимости 159 */ 160 template WrapDependencyFactory(F : ComponentFactory!(C, A), C, A...) 161 { 162 static if (A.length && is(A[0] == DependencyContainer)) 163 alias AA = A[1..$]; 164 else 165 alias AA = A; 166 167 class WrapDependencyFactory : ComponentFactory!(C, DependencyContainer, AA) 168 { 169 /** 170 * See_Also: ComponentFactory.createComponent 171 */ 172 C createComponent(DependencyContainer container, AA args) @safe 173 { 174 auto provider = new ClassProvider!(F, F)(container); 175 F factory; 176 provider.withProvided(true, (val) @trusted { 177 factory = cast(F)(*(cast(Object*)val)); 178 }); 179 static if (A.length && is(A[0] == DependencyContainer)) 180 return factory.createComponent(container, args); 181 else 182 return factory.createComponent(args); 183 } 184 } 185 } 186 187 @("Should work wrapDependencyFactory") 188 @safe unittest 189 { 190 alias F = ComponentFactoryCtor!(Server, HTTPServer, string, ushort); 191 auto cont = new DependencyContainer(); 192 auto factory = new WrapDependencyFactory!(F)(); 193 auto server = factory.createComponent(cont, "host", 3); 194 assert (server.host == "host"); 195 assert (server.port == 3); 196 197 auto factory2 = new WrapDependencyFactory!ServerFactory(); 198 server = factory.createComponent(cont, "host", 3); 199 assert (server.host == "host"); 200 assert (server.port == 3); 201 } 202