Add registration option for registering a class by concrete type when registering by supertype

This commit is contained in:
Mike Bierlee 2015-05-03 02:03:34 +02:00
parent d23230faae
commit 27e7b74e73
4 changed files with 84 additions and 10 deletions

View file

@ -1,5 +1,8 @@
Poodinis Changelog Poodinis Changelog
================== ==================
**Version 2.1.0**
* ADD option for registering a class by concrete type when registering that class by supertype.
**Version 2.0.0** **Version 2.0.0**
This version introduces changes which might be incompatible with your current codebase This version introduces changes which might be incompatible with your current codebase
* CHANGE dependency container to be synchronized. Sharing a dependency container between threads is now possible. * CHANGE dependency container to be synchronized. Sharing a dependency container between threads is now possible.

View file

@ -74,6 +74,13 @@ auto exampleClassInstance = dependencies.resolve!ExampleClass;
auto exampleClassInstance2 = dependencies.resolve!ExampleInterface; auto exampleClassInstance2 = dependencies.resolve!ExampleInterface;
assert(exampleClassInstance !is exampleClassInstance2); assert(exampleClassInstance !is exampleClassInstance2);
``` ```
You can solve this by adding the ADD_CONCRETE_TYPE_REGISTRATION option when registering:
```d
dependencies.register!(ExampleInterface, ExampleClass)(RegistrationOptions.ADD_CONCRETE_TYPE_REGISTRATION);
auto exampleClassInstance = dependencies.resolve!ExampleClass;
auto exampleClassInstance2 = dependencies.resolve!ExampleInterface;
assert(exampleClassInstance is exampleClassInstance2);
```
###Dependency scopes ###Dependency scopes
With dependency scopes, you can control how a dependency is resolved. The scope determines which instance is returned, be it the same each time or a new one. The following scopes are available: With dependency scopes, you can control how a dependency is resolved. The scope determines which instance is returned, be it the same each time or a new one. The following scopes are available:

View file

@ -33,6 +33,41 @@ class ResolveException : Exception {
} }
} }
/**
* Exception thrown when errors occur while registering a type in a dependency container.
*/
class RegistrationException : Exception {
this(string message, TypeInfo registrationType) {
super(format("Exception while registering type %s: %s", registrationType.toString(), message));
}
}
/**
* Options which influence the process of registering dependencies
*/
public enum RegistrationOptions {
/**
* When registering a type by its supertype, providing this option will also register
* a linked registration to the type itself.
*
* This allows you to resolve that type both by super type and concrete type using the
* same registration scope (and instance managed by this scope).
*
* Examples:
* ---
* class Cat : Animal { ... }
*
* container.register!(Animal, Cat)(RegistrationOptions.ADD_CONCRETE_TYPE_REGISTRATION);
*
* auto firstCat = container.resolve!(Animal, Cat);
* auto secondCat = container.resolve!Cat;
*
* assert(firstCat is secondCat);
* ---
*/
ADD_CONCRETE_TYPE_REGISTRATION
}
/** /**
* The dependency container maintains all dependencies registered with it. * The dependency container maintains all dependencies registered with it.
* *
@ -63,11 +98,7 @@ synchronized class DependencyContainer {
* Register and resolve a class by concrete type: * Register and resolve a class by concrete type:
* --- * ---
* class Cat : Animal { ... } * class Cat : Animal { ... }
*
* container.register!Cat; * container.register!Cat;
*
* container.resolve!Cat;
* container.resolve!(Animal, Cat); // Error! dependency is not registered by super type.
* --- * ---
* *
* See_Also: singleInstance, newInstance, existingInstance * See_Also: singleInstance, newInstance, existingInstance
@ -88,16 +119,12 @@ synchronized class DependencyContainer {
* Register and resolve by super type * Register and resolve by super type
* --- * ---
* class Cat : Animal { ... } * class Cat : Animal { ... }
*
* container.register!(Animal, Cat); * container.register!(Animal, Cat);
*
* container.resolve!(Animal, Cat);
* container.resolve!Cat; // Error! dependency is not registered by concrete type.
* --- * ---
* *
* See_Also: singleInstance, newInstance, existingInstance * See_Also: singleInstance, newInstance, existingInstance, RegistrationOptions
*/ */
public Registration register(SuperType, ConcreteType : SuperType)() { public Registration register(SuperType, ConcreteType : SuperType, RegistrationOptionsTuple...)(RegistrationOptionsTuple options) {
TypeInfo registeredType = typeid(SuperType); TypeInfo registeredType = typeid(SuperType);
TypeInfo_Class concreteType = typeid(ConcreteType); TypeInfo_Class concreteType = typeid(ConcreteType);
@ -112,10 +139,30 @@ synchronized class DependencyContainer {
auto newRegistration = new AutowiredRegistration!ConcreteType(registeredType, this); auto newRegistration = new AutowiredRegistration!ConcreteType(registeredType, this);
newRegistration.singleInstance(); newRegistration.singleInstance();
if (hasOption(options, RegistrationOptions.ADD_CONCRETE_TYPE_REGISTRATION)) {
static if (!is(SuperType == ConcreteType)) {
auto concreteTypeRegistration = register!ConcreteType;
concreteTypeRegistration.linkTo(newRegistration);
} else {
throw new RegistrationException("Option ADD_CONCRETE_TYPE_REGISTRATION cannot be used when registering a concrete type registration", concreteType);
}
}
registrations[registeredType] ~= cast(shared(Registration)) newRegistration; registrations[registeredType] ~= cast(shared(Registration)) newRegistration;
return newRegistration; return newRegistration;
} }
private bool hasOption(RegistrationOptionsTuple...)(RegistrationOptionsTuple options, RegistrationOptions option) {
foreach(presentOption ; options) {
if (presentOption == option) {
return true;
}
}
return false;
}
private Registration getExistingRegistration(TypeInfo registrationType, TypeInfo qualifierType) { private Registration getExistingRegistration(TypeInfo registrationType, TypeInfo qualifierType) {
auto existingCandidates = registrationType in registrations; auto existingCandidates = registrationType in registrations;
if (existingCandidates) { if (existingCandidates) {

View file

@ -426,4 +426,21 @@ version(unittest) {
assert(expectedTestClass is actualTestClass, "Instance resolved in main thread is not the one resolved in thread"); assert(expectedTestClass is actualTestClass, "Instance resolved in main thread is not the one resolved in thread");
} }
// Test registering type with option ADD_CONCRETE_TYPE_REGISTRATION
unittest {
shared(DependencyContainer) container = new DependencyContainer();
container.register!(TestInterface, TestClass)(RegistrationOptions.ADD_CONCRETE_TYPE_REGISTRATION);
auto firstInstance = container.resolve!TestInterface;
auto secondInstance = container.resolve!TestClass;
assert(firstInstance is secondInstance);
}
// Test registering concrete type with option ADD_CONCRETE_TYPE_REGISTRATION
unittest {
shared(DependencyContainer) container = new DependencyContainer();
assertThrown!RegistrationException(container.register!(TestClass, TestClass)(RegistrationOptions.ADD_CONCRETE_TYPE_REGISTRATION));
}
} }