diff --git a/source/poodinis/autowire.d b/source/poodinis/autowire.d index 45b2df0..218106a 100644 --- a/source/poodinis/autowire.d +++ b/source/poodinis/autowire.d @@ -161,7 +161,7 @@ private void autowireMember(string member, Type)(shared(DependencyContainer) con private QualifierType createOrResolveInstance(MemberType, QualifierType, bool createNew)(shared(DependencyContainer) container) { static if (createNew) { - auto instanceFactory = new NewInstanceScope(typeid(MemberType)); + auto instanceFactory = new InstanceFactory(typeid(MemberType), CreatesSingleton.no, null); return cast(MemberType) instanceFactory.getInstance(); } else { return container.resolve!(MemberType, QualifierType); diff --git a/source/poodinis/registration.d b/source/poodinis/registration.d index 353aa7e..5b8915e 100644 --- a/source/poodinis/registration.d +++ b/source/poodinis/registration.d @@ -13,11 +13,20 @@ module poodinis.registration; +import std.typecons; +import std.exception; + debug { import std.stdio; import std.string; } +class InstanceCreationException : Exception { + this(string message, string file = __FILE__, size_t line = __LINE__) { + super(message, file, line); + } +} + class Registration { private TypeInfo _registeredType = null; private TypeInfo_Class _instantiatableType = null; @@ -31,7 +40,7 @@ class Registration { return _instantiatableType; } - public CreationScope registationScope = null; + public InstanceFactory instanceFactory = null; this(TypeInfo registeredType, TypeInfo_Class instantiatableType) { this._registeredType = registeredType; @@ -44,11 +53,11 @@ class Registration { } - if (registationScope is null) { - throw new NoScopeDefinedException(registeredType); + if (instanceFactory is null) { + throw new InstanceCreationException("No instance factory defined for registration of type " ~ registeredType.toString()); } - return registationScope.getInstance(); + return instanceFactory.getInstance(); } public Registration linkTo(Registration registration) { @@ -57,46 +66,34 @@ class Registration { } } -class NoScopeDefinedException : Exception { - this(TypeInfo type) { - super("No scope defined for registration of type " ~ type.toString()); +alias CreatesSingleton = Flag!"CreatesSingleton"; + +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() { + 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) { - 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())); - } + writeln(format("DEBUG: Creating new instance of type %s", instanceType.toString())); } - + instance = instanceType.create(); return instance; } } @@ -107,53 +104,23 @@ class SingleInstanceScope : CreationScope { * Effectively makes the given registration a singleton. */ public Registration singleInstance(Registration registration) { - registration.registationScope = new SingleInstanceScope(registration.instantiatableType); + registration.instanceFactory = new InstanceFactory(registration.instantiatableType, CreatesSingleton.yes, null); 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); + registration.instanceFactory = new InstanceFactory(registration.instantiatableType, CreatesSingleton.no, null); 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); + registration.instanceFactory = new InstanceFactory(registration.instantiatableType, CreatesSingleton.yes, instance); return registration; } diff --git a/test/poodinis/registrationtest.d b/test/poodinis/registrationtest.d index 3017a9c..bd701a0 100644 --- a/test/poodinis/registrationtest.d +++ b/test/poodinis/registrationtest.d @@ -19,16 +19,7 @@ version(unittest) { // Test getting instance without scope defined throws exception unittest { Registration registration = new Registration(typeid(TestType), null); - 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"); + assertThrown!(InstanceCreationException)(registration.getInstance()); } // 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"); } - // 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)); @@ -60,15 +42,6 @@ version(unittest) { 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); @@ -90,4 +63,44 @@ version(unittest) { 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"); + } + }