Add registration linking

This commit is contained in:
Mike Bierlee 2015-05-03 00:46:51 +02:00
parent fe6a62104e
commit eb05f7702f
2 changed files with 254 additions and 229 deletions

View file

@ -1,151 +1,162 @@
/** /**
* This module contains objects for defining and scoping dependency registrations. * This module contains objects for defining and scoping dependency registrations.
* *
* Part of the Poodinis Dependency Injection framework. * Part of the Poodinis Dependency Injection framework.
* *
* Authors: * Authors:
* Mike Bierlee, m.bierlee@lostmoment.com * Mike Bierlee, m.bierlee@lostmoment.com
* Copyright: 2014-2015 Mike Bierlee * Copyright: 2014-2015 Mike Bierlee
* License: * License:
* This software is licensed under the terms of the MIT 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. * The full terms of the license can be found in the LICENSE file.
*/ */
module poodinis.registration; module poodinis.registration;
debug { debug {
import std.stdio; import std.stdio;
import std.string; import std.string;
} }
class Registration { class Registration {
TypeInfo registeredType = null; TypeInfo registeredType = null;
TypeInfo_Class instantiatableType = null; TypeInfo_Class instantiatableType = null;
CreationScope registationScope = null; CreationScope registationScope = null;
private Registration linkedRegistration;
this(TypeInfo registeredType, TypeInfo_Class instantiatableType) {
this.registeredType = registeredType; this(TypeInfo registeredType, TypeInfo_Class instantiatableType) {
this.instantiatableType = instantiatableType; this.registeredType = registeredType;
} this.instantiatableType = instantiatableType;
}
public Object getInstance(InstantiationContext context = new InstantiationContext()) {
if (registationScope is null) { public Object getInstance(InstantiationContext context = new InstantiationContext()) {
throw new NoScopeDefinedException(registeredType); if (linkedRegistration !is null) {
} return linkedRegistration.getInstance(context);
}
return registationScope.getInstance();
}
} if (registationScope is null) {
throw new NoScopeDefinedException(registeredType);
class NoScopeDefinedException : Exception { }
this(TypeInfo type) {
super("No scope defined for registration of type " ~ type.toString()); return registationScope.getInstance();
} }
}
public Registration linkTo(Registration registration) {
interface CreationScope { this.linkedRegistration = registration;
public Object getInstance(); return this;
} }
}
class NullScope : CreationScope {
public Object getInstance() { class NoScopeDefinedException : Exception {
debug(poodinisVerbose) { this(TypeInfo type) {
writeln("DEBUG: No instance created (NullScope)"); super("No scope defined for registration of type " ~ type.toString());
} }
return null; }
}
} interface CreationScope {
public Object getInstance();
class SingleInstanceScope : CreationScope { }
TypeInfo_Class instantiatableType = null;
Object instance = null; class NullScope : CreationScope {
public Object getInstance() {
this(TypeInfo_Class instantiatableType) { debug(poodinisVerbose) {
this.instantiatableType = instantiatableType; writeln("DEBUG: No instance created (NullScope)");
} }
return null;
public Object getInstance() { }
if (instance is null) { }
debug(poodinisVerbose) {
writeln(format("DEBUG: Creating new instance of type %s (SingleInstanceScope)", instantiatableType.toString())); class SingleInstanceScope : CreationScope {
} TypeInfo_Class instantiatableType = null;
instance = instantiatableType.create(); Object instance = null;
} else {
debug(poodinisVerbose) { this(TypeInfo_Class instantiatableType) {
writeln(format("DEBUG: Existing instance returned of type %s (SingleInstanceScope)", instantiatableType.toString())); this.instantiatableType = instantiatableType;
} }
}
public Object getInstance() {
if (instance is null) {
return instance; debug(poodinisVerbose) {
} writeln(format("DEBUG: Creating new instance of type %s (SingleInstanceScope)", instantiatableType.toString()));
} }
instance = instantiatableType.create();
/** } else {
* Scopes registrations to return the same instance every time a given registration is resolved. debug(poodinisVerbose) {
* writeln(format("DEBUG: Existing instance returned of type %s (SingleInstanceScope)", instantiatableType.toString()));
* Effectively makes the given registration a singleton. }
*/ }
public Registration singleInstance(Registration registration) {
registration.registationScope = new SingleInstanceScope(registration.instantiatableType);
return registration; return instance;
} }
}
class NewInstanceScope : CreationScope {
TypeInfo_Class instantiatableType = null; /**
* Scopes registrations to return the same instance every time a given registration is resolved.
this(TypeInfo_Class instantiatableType) { *
this.instantiatableType = instantiatableType; * Effectively makes the given registration a singleton.
} */
public Registration singleInstance(Registration registration) {
public Object getInstance() { registration.registationScope = new SingleInstanceScope(registration.instantiatableType);
debug(poodinisVerbose) { return registration;
writeln(format("DEBUG: Creating new instance of type %s (SingleInstanceScope)", instantiatableType.toString())); }
}
return instantiatableType.create(); class NewInstanceScope : CreationScope {
} TypeInfo_Class instantiatableType = null;
}
this(TypeInfo_Class instantiatableType) {
/** this.instantiatableType = instantiatableType;
* Scopes registrations to return a new instance every time the given registration is resolved. }
*/
public Registration newInstance(Registration registration) { public Object getInstance() {
registration.registationScope = new NewInstanceScope(registration.instantiatableType); debug(poodinisVerbose) {
return registration; writeln(format("DEBUG: Creating new instance of type %s (SingleInstanceScope)", instantiatableType.toString()));
} }
return instantiatableType.create();
class ExistingInstanceScope : CreationScope { }
Object instance = null; }
this(Object instance) { /**
this.instance = instance; * Scopes registrations to return a new instance every time the given registration is resolved.
} */
public Registration newInstance(Registration registration) {
public Object getInstance() { registration.registationScope = new NewInstanceScope(registration.instantiatableType);
debug(poodinisVerbose) { return registration;
writeln("DEBUG: Existing instance returned (ExistingInstanceScope)"); }
}
return instance; class ExistingInstanceScope : CreationScope {
} Object instance = null;
}
this(Object instance) {
/** this.instance = instance;
* Scopes registrations to return the given instance every time the given registration is resolved. }
*/
public Registration existingInstance(Registration registration, Object instance) { public Object getInstance() {
registration.registationScope = new ExistingInstanceScope(instance); debug(poodinisVerbose) {
return registration; writeln("DEBUG: Existing instance returned (ExistingInstanceScope)");
} }
return instance;
public string toConcreteTypeListString(Registration[] registrations) { }
auto concreteTypeListString = ""; }
foreach (registration ; registrations) {
if (concreteTypeListString.length > 0) { /**
concreteTypeListString ~= ", "; * Scopes registrations to return the given instance every time the given registration is resolved.
} */
concreteTypeListString ~= registration.instantiatableType.toString(); public Registration existingInstance(Registration registration, Object instance) {
} registration.registationScope = new ExistingInstanceScope(instance);
return concreteTypeListString; return registration;
} }
class InstantiationContext {} public string toConcreteTypeListString(Registration[] registrations) {
auto concreteTypeListString = "";
foreach (registration ; registrations) {
if (concreteTypeListString.length > 0) {
concreteTypeListString ~= ", ";
}
concreteTypeListString ~= registration.instantiatableType.toString();
}
return concreteTypeListString;
}
class InstantiationContext {}

View file

@ -1,80 +1,94 @@
/** /**
* Poodinis Dependency Injection Framework * Poodinis Dependency Injection Framework
* Copyright 2014-2015 Mike Bierlee * Copyright 2014-2015 Mike Bierlee
* This software is licensed under the terms of the MIT 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. * The full terms of the license can be found in the LICENSE file.
*/ */
import poodinis.registration; import poodinis.registration;
import std.exception; import std.exception;
version(unittest) { version(unittest) {
class TestType { class TestType {}
}
interface TestInterface {}
// Test getting instance without scope defined throws exception
unittest { class TestImplementation : TestInterface {}
// Test getting instance without scope defined throws exception
unittest {
Registration registration = new Registration(null, null); Registration registration = new Registration(null, null);
registration.registeredType = typeid(TestType); registration.registeredType = typeid(TestType);
assertThrown!(NoScopeDefinedException)(registration.getInstance()); assertThrown!(NoScopeDefinedException)(registration.getInstance());
} }
// Test getting instance from single instance scope // Test getting instance from single instance scope
unittest { unittest {
Registration registration = new Registration(null, null); Registration registration = new Registration(null, null);
registration.registationScope = new SingleInstanceScope(typeid(TestType)); registration.registationScope = new SingleInstanceScope(typeid(TestType));
auto instance1 = registration.getInstance(); auto instance1 = registration.getInstance();
auto instance2 = registration.getInstance(); auto instance2 = registration.getInstance();
assert(instance1 is instance2, "Registration with single instance scope did not return the same instance"); 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
unittest { unittest {
Registration registration = new Registration(null, typeid(TestType)); Registration registration = new Registration(null, typeid(TestType));
auto chainedRegistration = registration.singleInstance(); auto chainedRegistration = registration.singleInstance();
auto instance1 = registration.getInstance(); auto instance1 = registration.getInstance();
auto instance2 = registration.getInstance(); auto instance2 = registration.getInstance();
assert(instance1 is instance2, "Registration with single instance scope did not return the same instance"); assert(instance1 is instance2, "Registration with single instance scope did not return the same instance");
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 // Test getting instance from new instance scope
unittest { unittest {
Registration registration = new Registration(null, null); Registration registration = new Registration(null, null);
registration.registationScope = new NewInstanceScope(typeid(TestType)); registration.registationScope = new NewInstanceScope(typeid(TestType));
auto instance1 = registration.getInstance(); auto instance1 = registration.getInstance();
auto instance2 = registration.getInstance(); auto instance2 = registration.getInstance();
assert(instance1 !is instance2, "Registration with new instance scope did not return a different instance"); 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));
auto chainedRegistration = registration.newInstance(); auto chainedRegistration = registration.newInstance();
auto instance1 = registration.getInstance(); auto instance1 = registration.getInstance();
auto instance2 = registration.getInstance(); auto instance2 = registration.getInstance();
assert(instance1 !is instance2, "Registration with new instance scope did not return a different instance"); assert(instance1 !is instance2, "Registration with new instance scope did not return a different instance");
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 // Test getting instance from existing instance scope
unittest { unittest {
Registration registration = new Registration(null, null); Registration registration = new Registration(null, null);
TestType expectedInstance = new TestType(); TestType expectedInstance = new TestType();
registration.registationScope = new ExistingInstanceScope(expectedInstance); registration.registationScope = new ExistingInstanceScope(expectedInstance);
auto actualInstance = registration.getInstance(); auto actualInstance = registration.getInstance();
assert(expectedInstance is actualInstance, "Registration with existing instance did not return given instance"); 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);
auto expectedInstance = new TestType(); auto expectedInstance = new TestType();
auto chainedRegistration = registration.existingInstance(expectedInstance); auto chainedRegistration = registration.existingInstance(expectedInstance);
auto actualInstance = registration.getInstance(); auto actualInstance = registration.getInstance();
assert(expectedInstance is expectedInstance, "Registration with existing instance scope did not return the same instance"); assert(expectedInstance is expectedInstance, "Registration with existing instance scope did not return the same instance");
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 linking registrations
unittest {
Registration firstRegistration = new Registration(typeid(TestInterface), typeid(TestImplementation)).singleInstance();
Registration secondRegistration = new Registration(typeid(TestImplementation), typeid(TestImplementation)).singleInstance().linkTo(firstRegistration);
auto firstInstance = firstRegistration.getInstance();
auto secondInstance = secondRegistration.getInstance();
assert(firstInstance is secondInstance);
}
}