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