mirror of
https://github.com/mbierlee/poodinis.git
synced 2024-11-15 04:04:01 +01:00
Refactor registration scopes into simplified instance factory
This commit is contained in:
parent
0a682ccb16
commit
2ebef4c466
|
@ -161,7 +161,7 @@ private void autowireMember(string member, Type)(shared(DependencyContainer) con
|
||||||
|
|
||||||
private QualifierType createOrResolveInstance(MemberType, QualifierType, bool createNew)(shared(DependencyContainer) container) {
|
private QualifierType createOrResolveInstance(MemberType, QualifierType, bool createNew)(shared(DependencyContainer) container) {
|
||||||
static if (createNew) {
|
static if (createNew) {
|
||||||
auto instanceFactory = new NewInstanceScope(typeid(MemberType));
|
auto instanceFactory = new InstanceFactory(typeid(MemberType), CreatesSingleton.no, null);
|
||||||
return cast(MemberType) instanceFactory.getInstance();
|
return cast(MemberType) instanceFactory.getInstance();
|
||||||
} else {
|
} else {
|
||||||
return container.resolve!(MemberType, QualifierType);
|
return container.resolve!(MemberType, QualifierType);
|
||||||
|
|
|
@ -13,11 +13,20 @@
|
||||||
|
|
||||||
module poodinis.registration;
|
module poodinis.registration;
|
||||||
|
|
||||||
|
import std.typecons;
|
||||||
|
import std.exception;
|
||||||
|
|
||||||
debug {
|
debug {
|
||||||
import std.stdio;
|
import std.stdio;
|
||||||
import std.string;
|
import std.string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class InstanceCreationException : Exception {
|
||||||
|
this(string message, string file = __FILE__, size_t line = __LINE__) {
|
||||||
|
super(message, file, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class Registration {
|
class Registration {
|
||||||
private TypeInfo _registeredType = null;
|
private TypeInfo _registeredType = null;
|
||||||
private TypeInfo_Class _instantiatableType = null;
|
private TypeInfo_Class _instantiatableType = null;
|
||||||
|
@ -31,7 +40,7 @@ class Registration {
|
||||||
return _instantiatableType;
|
return _instantiatableType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CreationScope registationScope = null;
|
public InstanceFactory instanceFactory = null;
|
||||||
|
|
||||||
this(TypeInfo registeredType, TypeInfo_Class instantiatableType) {
|
this(TypeInfo registeredType, TypeInfo_Class instantiatableType) {
|
||||||
this._registeredType = registeredType;
|
this._registeredType = registeredType;
|
||||||
|
@ -44,11 +53,11 @@ class Registration {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (registationScope is null) {
|
if (instanceFactory is null) {
|
||||||
throw new NoScopeDefinedException(registeredType);
|
throw new InstanceCreationException("No instance factory defined for registration of type " ~ registeredType.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return registationScope.getInstance();
|
return instanceFactory.getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Registration linkTo(Registration registration) {
|
public Registration linkTo(Registration registration) {
|
||||||
|
@ -57,46 +66,34 @@ class Registration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NoScopeDefinedException : Exception {
|
alias CreatesSingleton = Flag!"CreatesSingleton";
|
||||||
this(TypeInfo type) {
|
|
||||||
super("No scope defined for registration of type " ~ type.toString());
|
class InstanceFactory {
|
||||||
|
private TypeInfo_Class instanceType = null;
|
||||||
|
private Object instance = null;
|
||||||
|
private CreatesSingleton createsSingleton;
|
||||||
|
|
||||||
|
this(TypeInfo_Class instanceType, CreatesSingleton createsSingleton = CreatesSingleton.yes, Object existingInstance = null) {
|
||||||
|
this.instanceType = instanceType;
|
||||||
|
this.createsSingleton = existingInstance !is null ? CreatesSingleton.yes : createsSingleton;
|
||||||
|
this.instance = existingInstance;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
interface CreationScope {
|
|
||||||
public Object getInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
class NullScope : CreationScope {
|
|
||||||
public Object getInstance() {
|
public Object getInstance() {
|
||||||
|
if (createsSingleton && instance !is null) {
|
||||||
|
debug(poodinisVerbose) {
|
||||||
|
writeln(format("DEBUG: Existing instance returned of type %s", instanceType.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
enforce!InstanceCreationException(instanceType, "Instance type is not defined, cannot create instance without knowing its type.");
|
||||||
debug(poodinisVerbose) {
|
debug(poodinisVerbose) {
|
||||||
writeln("DEBUG: No instance created (NullScope)");
|
writeln(format("DEBUG: Creating new instance of type %s", instanceType.toString()));
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SingleInstanceScope : CreationScope {
|
|
||||||
TypeInfo_Class instantiatableType = null;
|
|
||||||
Object instance = null;
|
|
||||||
|
|
||||||
this(TypeInfo_Class instantiatableType) {
|
|
||||||
this.instantiatableType = instantiatableType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getInstance() {
|
|
||||||
if (instance is null) {
|
|
||||||
debug(poodinisVerbose) {
|
|
||||||
writeln(format("DEBUG: Creating new instance of type %s (SingleInstanceScope)", instantiatableType.toString()));
|
|
||||||
}
|
|
||||||
instance = instantiatableType.create();
|
|
||||||
} else {
|
|
||||||
debug(poodinisVerbose) {
|
|
||||||
writeln(format("DEBUG: Existing instance returned of type %s (SingleInstanceScope)", instantiatableType.toString()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
instance = instanceType.create();
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,53 +104,23 @@ class SingleInstanceScope : CreationScope {
|
||||||
* Effectively makes the given registration a singleton.
|
* Effectively makes the given registration a singleton.
|
||||||
*/
|
*/
|
||||||
public Registration singleInstance(Registration registration) {
|
public Registration singleInstance(Registration registration) {
|
||||||
registration.registationScope = new SingleInstanceScope(registration.instantiatableType);
|
registration.instanceFactory = new InstanceFactory(registration.instantiatableType, CreatesSingleton.yes, null);
|
||||||
return registration;
|
return registration;
|
||||||
}
|
}
|
||||||
|
|
||||||
class NewInstanceScope : CreationScope {
|
|
||||||
TypeInfo_Class instantiatableType = null;
|
|
||||||
|
|
||||||
this(TypeInfo_Class instantiatableType) {
|
|
||||||
this.instantiatableType = instantiatableType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getInstance() {
|
|
||||||
debug(poodinisVerbose) {
|
|
||||||
writeln(format("DEBUG: Creating new instance of type %s (SingleInstanceScope)", instantiatableType.toString()));
|
|
||||||
}
|
|
||||||
return instantiatableType.create();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scopes registrations to return a new instance every time the given registration is resolved.
|
* Scopes registrations to return a new instance every time the given registration is resolved.
|
||||||
*/
|
*/
|
||||||
public Registration newInstance(Registration registration) {
|
public Registration newInstance(Registration registration) {
|
||||||
registration.registationScope = new NewInstanceScope(registration.instantiatableType);
|
registration.instanceFactory = new InstanceFactory(registration.instantiatableType, CreatesSingleton.no, null);
|
||||||
return registration;
|
return registration;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExistingInstanceScope : CreationScope {
|
|
||||||
Object instance = null;
|
|
||||||
|
|
||||||
this(Object instance) {
|
|
||||||
this.instance = instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getInstance() {
|
|
||||||
debug(poodinisVerbose) {
|
|
||||||
writeln("DEBUG: Existing instance returned (ExistingInstanceScope)");
|
|
||||||
}
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scopes registrations to return the given instance every time the given registration is resolved.
|
* Scopes registrations to return the given instance every time the given registration is resolved.
|
||||||
*/
|
*/
|
||||||
public Registration existingInstance(Registration registration, Object instance) {
|
public Registration existingInstance(Registration registration, Object instance) {
|
||||||
registration.registationScope = new ExistingInstanceScope(instance);
|
registration.instanceFactory = new InstanceFactory(registration.instantiatableType, CreatesSingleton.yes, instance);
|
||||||
return registration;
|
return registration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,16 +19,7 @@ version(unittest) {
|
||||||
// Test getting instance without scope defined throws exception
|
// Test getting instance without scope defined throws exception
|
||||||
unittest {
|
unittest {
|
||||||
Registration registration = new Registration(typeid(TestType), null);
|
Registration registration = new Registration(typeid(TestType), null);
|
||||||
assertThrown!(NoScopeDefinedException)(registration.getInstance());
|
assertThrown!(InstanceCreationException)(registration.getInstance());
|
||||||
}
|
|
||||||
|
|
||||||
// Test getting instance from single instance scope
|
|
||||||
unittest {
|
|
||||||
Registration registration = new Registration(null, null);
|
|
||||||
registration.registationScope = new SingleInstanceScope(typeid(TestType));
|
|
||||||
auto instance1 = registration.getInstance();
|
|
||||||
auto instance2 = registration.getInstance();
|
|
||||||
assert(instance1 is instance2, "Registration with single instance scope did not return the same instance");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test set single instance scope using scope setter
|
// Test set single instance scope using scope setter
|
||||||
|
@ -41,15 +32,6 @@ version(unittest) {
|
||||||
assert(registration is chainedRegistration, "Registration returned by scope setting is not the same as the registration being set");
|
assert(registration is chainedRegistration, "Registration returned by scope setting is not the same as the registration being set");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test getting instance from new instance scope
|
|
||||||
unittest {
|
|
||||||
Registration registration = new Registration(null, null);
|
|
||||||
registration.registationScope = new NewInstanceScope(typeid(TestType));
|
|
||||||
auto instance1 = registration.getInstance();
|
|
||||||
auto instance2 = registration.getInstance();
|
|
||||||
assert(instance1 !is instance2, "Registration with new instance scope did not return a different instance");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test set new instance scope using scope setter
|
// Test set new instance scope using scope setter
|
||||||
unittest {
|
unittest {
|
||||||
Registration registration = new Registration(null, typeid(TestType));
|
Registration registration = new Registration(null, typeid(TestType));
|
||||||
|
@ -60,15 +42,6 @@ version(unittest) {
|
||||||
assert(registration is chainedRegistration, "Registration returned by scope setting is not the same as the registration being set");
|
assert(registration is chainedRegistration, "Registration returned by scope setting is not the same as the registration being set");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test getting instance from existing instance scope
|
|
||||||
unittest {
|
|
||||||
Registration registration = new Registration(null, null);
|
|
||||||
TestType expectedInstance = new TestType();
|
|
||||||
registration.registationScope = new ExistingInstanceScope(expectedInstance);
|
|
||||||
auto actualInstance = registration.getInstance();
|
|
||||||
assert(expectedInstance is actualInstance, "Registration with existing instance did not return given instance");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test set existing instance scope using scope setter
|
// Test set existing instance scope using scope setter
|
||||||
unittest {
|
unittest {
|
||||||
Registration registration = new Registration(null, null);
|
Registration registration = new Registration(null, null);
|
||||||
|
@ -90,4 +63,44 @@ version(unittest) {
|
||||||
assert(firstInstance is secondInstance);
|
assert(firstInstance is secondInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test instance factory with singletons
|
||||||
|
unittest {
|
||||||
|
auto factory = new InstanceFactory(typeid(TestImplementation), CreatesSingleton.yes, null);
|
||||||
|
auto instanceOne = factory.getInstance();
|
||||||
|
auto instanceTwo = factory.getInstance();
|
||||||
|
|
||||||
|
assert(instanceOne !is null, "Created factory instance is null");
|
||||||
|
assert(instanceOne is instanceTwo, "Created factory instance is not the same");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test instance factory with new instances
|
||||||
|
unittest {
|
||||||
|
auto factory = new InstanceFactory(typeid(TestImplementation), CreatesSingleton.no, null);
|
||||||
|
auto instanceOne = factory.getInstance();
|
||||||
|
auto instanceTwo = factory.getInstance();
|
||||||
|
|
||||||
|
assert(instanceOne !is null, "Created factory instance is null");
|
||||||
|
assert(instanceOne !is instanceTwo, "Created factory instance is the same");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test instance factory with existing instances
|
||||||
|
unittest {
|
||||||
|
auto existingInstance = new TestImplementation();
|
||||||
|
auto factory = new InstanceFactory(typeid(TestImplementation), CreatesSingleton.yes, existingInstance);
|
||||||
|
auto instanceOne = factory.getInstance();
|
||||||
|
auto instanceTwo = factory.getInstance();
|
||||||
|
|
||||||
|
assert(instanceOne is existingInstance, "Created factory instance is not the existing instance");
|
||||||
|
assert(instanceTwo is existingInstance, "Created factory instance is not the existing instance when called again");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test instance factory with existing instances when setting singleton flag to "no"
|
||||||
|
unittest {
|
||||||
|
auto existingInstance = new TestImplementation();
|
||||||
|
auto factory = new InstanceFactory(typeid(TestImplementation), CreatesSingleton.no, existingInstance);
|
||||||
|
auto instance = factory.getInstance();
|
||||||
|
|
||||||
|
assert(instance is existingInstance, "Created factory instance is not the existing instance");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue