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