Deprecate singleton factory method

The factory method encourages misuse of dependency injection (by using it as a service locator). Removing the factory method forces the user to make this choice deliberately.
This commit is contained in:
Mike Bierlee 2016-08-08 22:17:17 +02:00
parent 02dbe20c64
commit 9e5a27d046
13 changed files with 311 additions and 308 deletions

View file

@ -4,6 +4,9 @@ Poodinis Changelog
* REMOVE deprecated registration options. They are still available in properly cased forms.
* REMOVE deprecated register() and resolve() methods which accept variadics and arrays for options.
Since registration and resolve options have become bitfields, you should specify them with logical ANDs.
* DEPRECATE DependencyContainer.getInstance(). To properly make use of inversion of control, one should only
create the dependency container once during set-up and then completly rely on injection. See examples for
proper usage. You can still create your own singleton factory (method) if this is crucial to your design.
**Version 6.3.0**
* CHANGE registration and resolve options to be supplied using bit flags instead. (Thanks to tmccombs)

View file

@ -35,7 +35,7 @@ class DataWriter {
}
void main() {
auto dependencies = DependencyContainer.getInstance();
auto dependencies = new shared DependencyContainer();
dependencies.register!DataWriter;
dependencies.register!(Database, RelationalDatabase);

View file

@ -6,11 +6,10 @@ The container
-------------
To register a class, a new dependency container must be instantiated:
```d
// Register a private container
shared(DependencyContainer) dependencies = new DependencyContainer();
// Or use the singleton container
dependencies = DependencyContainer.getInstance();
// Create a shared container
auto dependencies = new shared DependencyContainer();
```
A shared dependency container is thread-safe and resolves the same dependencies across all threads.
###Registering dependencies
To make dependencies available, they have to be registered:
```d

View file

@ -53,7 +53,7 @@ class SecurityManager {
}
void main() {
auto dependencies = DependencyContainer.getInstance();
auto dependencies = new shared DependencyContainer();
dependencies.register!SuperSecurityDevice; // Registered with the default "Single instance" scope
dependencies.register!SecurityManager;

View file

@ -60,7 +60,7 @@ class ExampleApplicationContext : ApplicationContext {
}
void main() {
auto container = DependencyContainer.getInstance();
auto container = new shared DependencyContainer();
container.registerContext!ExampleApplicationContext;
auto townSquare = container.resolve!TownSquare;

View file

@ -43,7 +43,7 @@ class PieEater {
}
void main() {
auto dependencies = DependencyContainer.getInstance();
auto dependencies = new shared DependencyContainer();
dependencies.register!(Pie, BlueBerryPie);
dependencies.register!(Pie, ApplePie);
dependencies.register!(Pie, CardboardBoxPie);

View file

@ -44,7 +44,7 @@ class HybridCar {
}
void main() {
auto dependencies = DependencyContainer.getInstance();
auto dependencies = new shared DependencyContainer();
dependencies.register!HybridCar;
dependencies.register!(Engine, FuelEngine);

View file

@ -16,7 +16,7 @@ class DataWriter {
}
void main() {
auto dependencies = DependencyContainer.getInstance();
auto dependencies = new shared DependencyContainer();
dependencies.register!DataWriter;
dependencies.register!(Database, RelationalDatabase);

View file

@ -26,7 +26,7 @@ class Orchestra {
}
void main() {
auto dependencies = DependencyContainer.getInstance();
auto dependencies = new shared DependencyContainer();
/*
* By using the resolve option "registerBeforeResolving" you can register the resolved class

View file

@ -389,8 +389,9 @@ synchronized class DependencyContainer {
/**
* Returns a global singleton instance of a dependency container.
* Deprecated: create new instance with new keyword or implement your own singleton factory (method)
*/
public static shared(DependencyContainer) getInstance() {
deprecated public static shared(DependencyContainer) getInstance() {
static shared DependencyContainer instance;
if (instance is null) {
instance = new DependencyContainer();

View file

@ -89,7 +89,7 @@ version(unittest) {
// Test autowiring concrete type to existing instance
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!ComponentA;
auto componentB = new ComponentB();
container.autowire(componentB);
@ -98,7 +98,7 @@ version(unittest) {
// Test autowiring interface type to existing instance
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!(InterfaceA, ComponentC);
auto componentD = new ComponentD();
container.autowire(componentD);
@ -107,7 +107,7 @@ version(unittest) {
// Test autowiring private members
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!(InterfaceA, ComponentC);
auto componentD = new ComponentD();
container.autowire(componentD);
@ -116,7 +116,7 @@ version(unittest) {
// Test autowiring will only happen once
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!(InterfaceA, ComponentC).newInstance();
auto componentD = new ComponentD();
container.autowire(componentD);
@ -128,14 +128,14 @@ version(unittest) {
// Test autowiring unregistered type
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
auto componentD = new ComponentD();
assertThrown!(ResolveException)(container.autowire(componentD), "Autowiring unregistered type should throw ResolveException");
}
// Test autowiring member with non-autowire attribute does not autowire
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
auto componentE = new ComponentE();
container.autowire(componentE);
assert(componentE.componentC is null, "Autowiring should not occur for members with attributes other than @Autowire");
@ -143,7 +143,7 @@ version(unittest) {
// Test autowire class with alias declaration
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!ComponentA;
auto componentDeclarationCocktail = new ComponentDeclarationCocktail();
@ -154,7 +154,7 @@ version(unittest) {
// Test autowire class with qualifier
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!(InterfaceA, ComponentC);
container.register!(InterfaceA, ComponentX);
auto componentX = container.resolve!(InterfaceA, ComponentX);
@ -167,7 +167,7 @@ version(unittest) {
// Test autowire class with multiple qualifiers
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!(InterfaceA, ComponentC);
container.register!(InterfaceA, ComponentX);
auto componentC = container.resolve!(InterfaceA, ComponentC);
@ -182,7 +182,7 @@ version(unittest) {
// Test getting instance from autowired registration will autowire instance
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!ComponentA;
auto registration = new AutowiredRegistration!ComponentB(typeid(ComponentB), container).singleInstance();
@ -193,7 +193,7 @@ version(unittest) {
// Test autowiring a dynamic array with all qualified types
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!(InterfaceA, ComponentC);
container.register!(InterfaceA, ComponentX);
@ -205,7 +205,7 @@ version(unittest) {
// Test autowiring new instance of singleinstance registration with newInstance UDA
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!ComponentA;
auto regularComponentA = container.resolve!ComponentA;
@ -218,7 +218,7 @@ version(unittest) {
// Test autowiring members from base class
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!ComponentA;
container.register!ComponentB;
container.register!ComponentZ;
@ -231,7 +231,7 @@ version(unittest) {
// Test autowiring optional depenencies
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
auto instance = new OuttaTime();
container.autowire(instance);

View file

@ -182,14 +182,14 @@ version(unittest) {
// Test register concrete type
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
auto registration = container.register!TestClass;
assert(registration.registeredType == typeid(TestClass), "Type of registered type not the same");
}
// Test resolve registered type
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!TestClass;
TestClass actualInstance = container.resolve!TestClass;
assert(actualInstance !is null, "Resolved type is null");
@ -198,7 +198,7 @@ version(unittest) {
// Test register interface
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!(TestInterface, TestClass);
TestInterface actualInstance = container.resolve!TestInterface;
assert(actualInstance !is null, "Resolved type is null");
@ -207,19 +207,19 @@ version(unittest) {
// Test resolve non-registered type
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
assertThrown!ResolveException(container.resolve!TestClass, "Resolving non-registered type does not fail");
}
// Test clear registrations
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!TestClass;
container.clearAllRegistrations();
assertThrown!ResolveException(container.resolve!TestClass, "Resolving cleared type does not fail");
}
// Test get singleton of container
// Test get singleton of container (DEPRECATED)
unittest {
auto instance1 = DependencyContainer.getInstance();
auto instance2 = DependencyContainer.getInstance();
@ -228,7 +228,7 @@ version(unittest) {
// Test resolve single instance for type
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!TestClass.singleInstance();
auto instance1 = container.resolve!TestClass;
auto instance2 = container.resolve!TestClass;
@ -237,7 +237,7 @@ version(unittest) {
// Test resolve new instance for type
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!TestClass.newInstance();
auto instance1 = container.resolve!TestClass;
auto instance2 = container.resolve!TestClass;
@ -246,7 +246,7 @@ version(unittest) {
// Test resolve existing instance for type
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
auto expectedInstance = new TestClass();
container.register!TestClass.existingInstance(expectedInstance);
auto actualInstance = container.resolve!TestClass;
@ -255,7 +255,7 @@ version(unittest) {
// Test autowire resolved instances
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!AutowiredClass;
container.register!ComponentClass;
auto componentInstance = container.resolve!ComponentClass;
@ -265,7 +265,7 @@ version(unittest) {
// Test circular autowiring
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!ComponentMouse;
container.register!ComponentCat;
auto mouse = container.resolve!ComponentMouse;
@ -275,7 +275,7 @@ version(unittest) {
// Test remove registration
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!TestClass;
container.removeRegistration!TestClass;
assertThrown!ResolveException(container.resolve!TestClass);
@ -283,7 +283,7 @@ version(unittest) {
// Test autowiring does not autowire member where instance is non-null
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
auto existingA = new AutowiredClass();
auto existingB = new ComponentClass();
existingB.autowiredClass = existingA;
@ -298,7 +298,7 @@ version(unittest) {
// Test autowiring circular dependency by third-degree
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!Eenie;
container.register!Meenie;
container.register!Moe;
@ -310,7 +310,7 @@ version(unittest) {
// Test autowiring deep circular dependencies
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!Ittie;
container.register!Bittie;
container.register!Banana;
@ -322,7 +322,7 @@ version(unittest) {
// Test autowiring deep circular dependencies with newInstance scope does not autowire new instance second time
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!Ittie.newInstance();
container.register!Bittie.newInstance();
container.register!Banana.newInstance();
@ -334,7 +334,7 @@ version(unittest) {
// Test autowiring type registered by interface
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!Banana;
container.register!Bittie;
container.register!(SuperInterface, SuperImplementation);
@ -346,7 +346,7 @@ version(unittest) {
// Test reusing a container after clearing all registrations
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!Banana;
container.clearAllRegistrations();
try {
@ -360,14 +360,14 @@ version(unittest) {
// Test register multiple concrete classess to same interface type
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!(Color, Blue);
container.register!(Color, Red);
}
// Test removing all registrations for type with multiple registrations.
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!(Color, Blue);
container.register!(Color, Red);
container.removeRegistration!Color;
@ -375,7 +375,7 @@ version(unittest) {
// Test registering same registration again
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
auto firstRegistration = container.register!(Color, Blue);
auto secondRegistration = container.register!(Color, Blue);
@ -384,7 +384,7 @@ version(unittest) {
// Test resolve registration with multiple qualifiers
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!(Color, Blue);
container.register!(Color, Red);
try {
@ -397,7 +397,7 @@ version(unittest) {
// Test resolve registration with multiple qualifiers using a qualifier
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!(Color, Blue);
container.register!(Color, Red);
auto blueInstance = container.resolve!(Color, Blue);
@ -410,7 +410,7 @@ version(unittest) {
// Test autowire of unqualified member typed by interface.
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!Spiders;
container.register!(TestInterface, TestClass);
@ -421,7 +421,7 @@ version(unittest) {
// Register existing registration
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
auto firstRegistration = container.register!TestClass;
auto secondRegistration = container.register!TestClass;
@ -431,7 +431,7 @@ version(unittest) {
// Register existing registration by supertype
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
auto firstRegistration = container.register!(TestInterface, TestClass);
auto secondRegistration = container.register!(TestInterface, TestClass);
@ -441,7 +441,7 @@ version(unittest) {
// Resolve dependency depending on itself
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!Recursive;
auto instance = container.resolve!Recursive;
@ -452,7 +452,7 @@ version(unittest) {
// Test autowire stack pop-back
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!Moolah;
container.register!Wants.newInstance();
container.register!John;
@ -465,7 +465,7 @@ version(unittest) {
// Test resolving registration registered in different thread
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
auto thread = new Thread(delegate() {
container.register!TestClass;
@ -478,7 +478,7 @@ version(unittest) {
// Test resolving instance previously resolved in different thread
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
shared(TestClass) actualTestClass;
container.register!TestClass;
@ -496,7 +496,7 @@ version(unittest) {
// Test registering type with option doNotAddConcreteTypeRegistration
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!(TestInterface, TestClass)(RegistrationOption.doNotAddConcreteTypeRegistration);
auto firstInstance = container.resolve!TestInterface;
@ -505,14 +505,14 @@ version(unittest) {
// Test registering conrete type with registration option doNotAddConcreteTypeRegistration does nothing
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!TestClass(RegistrationOption.doNotAddConcreteTypeRegistration);
container.resolve!TestClass;
}
// Test registering type will register by contrete type by default
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!(TestInterface, TestClass);
auto firstInstance = container.resolve!TestInterface;
@ -523,7 +523,7 @@ version(unittest) {
// Test resolving all registrations to an interface
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!(Color, Blue);
container.register!(Color, Red);
@ -534,7 +534,7 @@ version(unittest) {
// Test autowiring instances resolved in array
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!UnrelatedClass;
container.register!(TestInterface, TestClassDeux);
@ -546,7 +546,7 @@ version(unittest) {
// Test setting up simple dependencies through application context
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.registerContext!TestContext;
auto instance = container.resolve!TestClass;
@ -555,7 +555,7 @@ version(unittest) {
// Test resolving dependency from registered application context
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.registerContext!TestContext;
auto instance = container.resolve!UnrelatedClass;
@ -564,7 +564,7 @@ version(unittest) {
// Test autowiring application context
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.register!UnrelatedClass;
container.registerContext!AutowiredTestContext;
auto instance = container.resolve!ClassWrapper;
@ -575,7 +575,7 @@ version(unittest) {
// Test autowiring application context with dependencies registered in same context
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.registerContext!ComplexAutowiredTestContext;
auto instance = container.resolve!ClassWrapperWrapper;
auto wrapper = container.resolve!ClassWrapper;
@ -588,14 +588,14 @@ version(unittest) {
// Test resolving registered context
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.registerContext!TestContext;
container.resolve!ApplicationContext;
}
// Test set persistent registration options
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.setPersistentRegistrationOptions(RegistrationOption.doNotAddConcreteTypeRegistration);
container.register!(TestInterface, TestClass);
assertThrown!ResolveException(container.resolve!TestClass);
@ -603,7 +603,7 @@ version(unittest) {
// Test unset persistent registration options
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.setPersistentRegistrationOptions(RegistrationOption.doNotAddConcreteTypeRegistration);
container.unsetPersistentRegistrationOptions();
container.register!(TestInterface, TestClass);
@ -612,21 +612,21 @@ version(unittest) {
// Test registration when resolving
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.resolve!(TestInterface, TestClass)(ResolveOption.registerBeforeResolving);
container.resolve!TestClass;
}
// Test set persistent resolve options
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.setPersistentResolveOptions(ResolveOption.registerBeforeResolving);
container.resolve!TestClass;
}
// Test unset persistent resolve options
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
container.setPersistentResolveOptions(ResolveOption.registerBeforeResolving);
container.unsetPersistentResolveOptions();
assertThrown!ResolveException(container.resolve!TestClass);
@ -634,20 +634,20 @@ version(unittest) {
// Test ResolveOption registerBeforeResolving fails for interfaces
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
assertThrown!ResolveException(container.resolve!TestInterface(ResolveOption.registerBeforeResolving));
}
// Test ResolveOption noResolveException does not throw
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
auto instance = container.resolve!TestInterface(ResolveOption.noResolveException);
assert(instance is null);
}
// ResolveOption noResolveException does not throw for resolveAll
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
auto instances = container.resolveAll!TestInterface(ResolveOption.noResolveException);
assert(instances.length == 0);
}

View file

@ -87,7 +87,7 @@ version(unittest) {
//Test register component registrations from context
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
auto context = new TestContext();
context.registerContextComponents(container);
auto bananaInstance = container.resolve!Banana;
@ -97,7 +97,7 @@ version(unittest) {
//Test non-annotated methods are not registered
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
auto context = new TestContext();
context.registerContextComponents(container);
assertThrown!ResolveException(container.resolve!Apple);
@ -105,7 +105,7 @@ version(unittest) {
//Test register component by base type
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
auto context = new TestContext();
context.registerContextComponents(container);
auto instance = container.resolve!Fruit;
@ -114,7 +114,7 @@ version(unittest) {
//Test register components with multiple candidates
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
auto context = new TestContext();
context.registerContextComponents(container);
@ -127,7 +127,7 @@ version(unittest) {
//Test register component as prototype
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto container = new shared DependencyContainer();
auto context = new TestContext();
context.registerContextComponents(container);