Refactor registration scopes into simplified instance factory

This commit is contained in:
Mike Bierlee 2015-12-23 17:51:55 +01:00
parent 0a682ccb16
commit 2ebef4c466
3 changed files with 80 additions and 100 deletions

View file

@ -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);

View file

@ -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());
}
}
interface CreationScope { class InstanceFactory {
public Object getInstance(); private TypeInfo_Class instanceType = null;
} private Object instance = null;
private CreatesSingleton createsSingleton;
class NullScope : CreationScope { this(TypeInfo_Class instanceType, CreatesSingleton createsSingleton = CreatesSingleton.yes, Object existingInstance = null) {
public Object getInstance() { this.instanceType = instanceType;
debug(poodinisVerbose) { this.createsSingleton = existingInstance !is null ? CreatesSingleton.yes : createsSingleton;
writeln("DEBUG: No instance created (NullScope)"); this.instance = existingInstance;
}
return null;
}
}
class SingleInstanceScope : CreationScope {
TypeInfo_Class instantiatableType = null;
Object instance = null;
this(TypeInfo_Class instantiatableType) {
this.instantiatableType = instantiatableType;
} }
public Object getInstance() { public Object getInstance() {
if (instance is null) { if (createsSingleton && instance !is null) {
debug(poodinisVerbose) { debug(poodinisVerbose) {
writeln(format("DEBUG: Creating new instance of type %s (SingleInstanceScope)", instantiatableType.toString())); writeln(format("DEBUG: Existing instance returned of type %s", instanceType.toString()));
}
instance = instantiatableType.create();
} else {
debug(poodinisVerbose) {
writeln(format("DEBUG: Existing instance returned of type %s (SingleInstanceScope)", instantiatableType.toString()));
}
} }
return instance;
}
enforce!InstanceCreationException(instanceType, "Instance type is not defined, cannot create instance without knowing its type.");
debug(poodinisVerbose) {
writeln(format("DEBUG: Creating new instance of type %s", instanceType.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;
} }

View file

@ -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");
}
} }