mirror of
https://github.com/mbierlee/poodinis.git
synced 2024-11-15 04:04:01 +01:00
e1f0cca5c5
Fixes issue #12
159 lines
4.3 KiB
D
159 lines
4.3 KiB
D
/**
|
|
* This module contains instance factory facilities
|
|
*
|
|
* Authors:
|
|
* Mike Bierlee, m.bierlee@lostmoment.com
|
|
* Copyright: 2014-2016 Mike Bierlee
|
|
* License:
|
|
* This software is licensed under the terms of the MIT license.
|
|
* The full terms of the license can be found in the LICENSE file.
|
|
*/
|
|
|
|
module poodinis.factory;
|
|
|
|
import poodinis.container;
|
|
|
|
import std.typecons;
|
|
import std.exception;
|
|
import std.traits;
|
|
import std.meta;
|
|
import std.string;
|
|
|
|
debug {
|
|
import std.stdio;
|
|
}
|
|
|
|
alias CreatesSingleton = Flag!"CreatesSingleton";
|
|
alias InstanceFactoryMethod = Object delegate();
|
|
|
|
class InstanceCreationException : Exception {
|
|
this(string message, string file = __FILE__, size_t line = __LINE__) {
|
|
super(message, file, line);
|
|
}
|
|
}
|
|
|
|
struct InstanceFactoryParameters {
|
|
TypeInfo_Class instanceType;
|
|
CreatesSingleton createsSingleton = CreatesSingleton.yes;
|
|
Object existingInstance;
|
|
InstanceFactoryMethod factoryMethod;
|
|
}
|
|
|
|
class InstanceFactory {
|
|
private Object instance = null;
|
|
private InstanceFactoryParameters _factoryParameters;
|
|
|
|
this() {
|
|
factoryParameters = InstanceFactoryParameters();
|
|
}
|
|
|
|
public @property void factoryParameters(InstanceFactoryParameters factoryParameters) {
|
|
if (factoryParameters.factoryMethod is null) {
|
|
factoryParameters.factoryMethod = &this.createInstance;
|
|
}
|
|
|
|
if (factoryParameters.existingInstance !is null) {
|
|
factoryParameters.createsSingleton = CreatesSingleton.yes;
|
|
this.instance = factoryParameters.existingInstance;
|
|
}
|
|
|
|
_factoryParameters = factoryParameters;
|
|
}
|
|
|
|
public @property InstanceFactoryParameters factoryParameters() {
|
|
return _factoryParameters;
|
|
}
|
|
|
|
public Object getInstance() {
|
|
if (_factoryParameters.createsSingleton && instance !is null) {
|
|
debug(poodinisVerbose) {
|
|
writeln(format("DEBUG: Existing instance returned of type %s", _factoryParameters.instanceType.toString()));
|
|
}
|
|
|
|
return instance;
|
|
}
|
|
|
|
debug(poodinisVerbose) {
|
|
writeln(format("DEBUG: Creating new instance of type %s", _factoryParameters.instanceType.toString()));
|
|
}
|
|
|
|
instance = _factoryParameters.factoryMethod();
|
|
return instance;
|
|
}
|
|
|
|
protected Object createInstance() {
|
|
enforce!InstanceCreationException(_factoryParameters.instanceType, "Instance type is not defined, cannot create instance without knowing its type.");
|
|
return _factoryParameters.instanceType.create();
|
|
}
|
|
}
|
|
|
|
class ConstructorInjectingInstanceFactory(InstanceType) : InstanceFactory {
|
|
private shared DependencyContainer container;
|
|
private bool isBeingInjected = false;
|
|
|
|
this(shared DependencyContainer container) {
|
|
this.container = container;
|
|
}
|
|
|
|
private static string createArgumentList(Params...)() {
|
|
string argumentList = "";
|
|
foreach(param; Params) {
|
|
if (argumentList.length > 0) {
|
|
argumentList ~= ",";
|
|
}
|
|
|
|
argumentList ~= "container.resolve!" ~ param.stringof;
|
|
}
|
|
return argumentList;
|
|
}
|
|
|
|
private static string createImportList(Params...)() {
|
|
string importList = "";
|
|
foreach(param; Params) {
|
|
importList ~= "import " ~ moduleName!param ~ ";";
|
|
}
|
|
return importList;
|
|
}
|
|
|
|
private static bool parametersAreValid(Params...)() {
|
|
bool isValid = true;
|
|
foreach(param; Params) {
|
|
if (isBuiltinType!param) {
|
|
isValid = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return isValid;
|
|
}
|
|
|
|
protected override Object createInstance() {
|
|
enforce!InstanceCreationException(container, "A dependency container is not defined. Cannot perform constructor injection without one.");
|
|
enforce!InstanceCreationException(!isBeingInjected, format("%s is already being created and injected; possible circular dependencies in constructors?", InstanceType.stringof));
|
|
|
|
Object instance = null;
|
|
static if (__traits(compiles, __traits(getOverloads, InstanceType, `__ctor`))) {
|
|
foreach(ctor ; __traits(getOverloads, InstanceType, `__ctor`)) {
|
|
static if (parametersAreValid!(Parameters!ctor)) {
|
|
isBeingInjected = true;
|
|
mixin(`
|
|
import ` ~ moduleName!InstanceType ~ `;
|
|
` ~ createImportList!(Parameters!ctor) ~ `
|
|
instance = new ` ~ fullyQualifiedName!InstanceType ~ `(` ~ createArgumentList!(Parameters!ctor) ~ `);
|
|
`);
|
|
isBeingInjected = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (instance is null) {
|
|
instance = typeid(InstanceType).create();
|
|
}
|
|
|
|
enforce!InstanceCreationException(instance !is null, "Unable to create instance of type" ~ InstanceType.stringof ~ ", does it have injectable constructors?");
|
|
|
|
return instance;
|
|
}
|
|
}
|