Limit the use of scopes or more concretely Use scopes for wiring only.
Used properly, scopes can reduce a lot of factory boiler plate. I use scopes to wire together sub-processes that may need access to its name and arguments. This is similar to the RequestScope
s provided by Guice and Spring.
But scopes are effectively a thread-local map of string to object. This is practically a Global Variable Depot. This is why I limit my scope usage.
This leads to the corollary:
Hide the scopes or more generally Hide your DI Framework
Because scopes (and DI frameworks) are essentially Global Variable Depots, I prefer to encapsulate the DIF such that the only thing that knows there is a DIF is the main method.
To do this, I define my own scope interface and my own ProcessFactory that I define within a Guice module.
Because of this, my process manager is free of references to Guice. In fact, it is little more than this method:
void run(final ProcessContext context) {
try {
this.scope.enter(context.processArgs());
this.factory.create(args.processName());
} finally {
this.scope.exit();
}
}
Here's the complete Guice module that binds and hides my use of a Guice. It implements my own ProcessScope and ProcessFactory interfaces. It binds @ProcessParameters so they can be injected and a few convienience objects as well (@ProcessName String and ProcessConfig).
public class ProcessRunnerModule extends AbstractModule {
private static final String PROCESS_RUN_SCOPE = "ProcessRunScope";
/**
* Objects of type Map<String, Object> parameterized by @ProcessParameters
* are injected in the ProcessRunScope
*/
static final Key<Map<String, Object>> PROCESS_PARAMETERS_KEY;
static {
final TypeLiteral<Map<String, Object>> MAP_TYPE = new TypeLiteral<Map<String, Object>>() {
};
PROCESS_PARAMETERS_KEY = Key.get(MAP_TYPE, ProcessParameters.class);
}
/**
* Wraps Guice scope to ProcessScope. Injects the @ProcessParameters into guice
*/
private static class GuiceScopeAdapter implements ProcessScope {
private final SimpleScope scope;
@Inject @SuppressWarnings("unused")
public GuiceScopeAdapter(@Named(PROCESS_RUN_SCOPE) SimpleScope scope) {
this.scope = scope;
}
@Override
public void enterScope(Map<String, Object> parameters) {
scope.enter();
scope.seed(PROCESS_PARAMETERS_KEY, parameters);
}
@Override
public void exitScope() {
scope.exit();
}
}
/**
* Processes are run and bound in @ProcessRunScope.
*/
protected void configure() {
final SimpleScope processRunScope = new SimpleScope();
bindScope(ProcessRunScope.class, processRunScope);
bind(SimpleScope.class)
.annotatedWith(Names.named(PROCESS_RUN_SCOPE))
.to(processRunScope);
}
/**
* This wraps Processes bound via MapBinder to a ProcessFactory
*/
@Provides @Singleton
ProcessFactory createProcessFactory(final Map<String, Provider<Process>> processFactories) {
log.info("Instantiating process factory", "known-processes", processFactories.keySet());
return new ProcessFactory() {
@Override
public Process create(final String name) {
return processFactories.get(name).get();
}
};
}
/**
* ProcessRunner does not know about Guice
*/
@Provides @Singleton
ProcessRunner createProcessRunner(
final ProcessScope processScope,
final ProcessFactory processFactory) {
return new ProcessRunner(processScope, processFactory);
}
/**
* Convienience: bind a @ProcessName extracted from the @ProcessParameters
*/
@Provides @ProcessName @ProcessRunScope
String bindProcessName(final @ProcessParameters Map<String, Object> params) {
return params.get(ProcessRunner.PROCESS_NAME_KEY).toString();
}
/**
* Convienience: bind a ProcessConfig wrapping the @ProcessParameters
*/
@Provides @ProcessRunScope
ProcessConfig createParamHelper(final @ProcessParameters Map<String, Object> params) {
return new ProcessConfig(params);
}
}
If you want to enforce that there is only a single instance of some class, then you must use the Singleton pattern. (aside from the question if your analysis is correct that you will only ever need a single instance.)
The C++ smart pointers auto_ptr<>
and unique_ptr<>
enforce a completely different contract, namely that there is only one reference (or pointer) to a given instance.
For example, this code is perfectly legal, but there are clearly two instances of Resource
:
class Resource {
// ...
};
std::unique_ptr<Resource> instance1(new Resource());
std::unique_ptr<Resource> instance2(new Resource());
Best Answer
This is called the Singleton Pattern and is considered to be an anti-pattern by many people nowadays, since it hides dependencies and makes your code less maintainable.
The necessity for using the singleton pattern may arise from other bad design decisions, but I cannot judge since I know nothing about your architecture and design.
Anyway, let me give you an example how to get rid of this pattern. In a game there will most likely be some kind of main loop which calls everything else. For simplicity let's assume this main loop is contained in an object, but this is not a must. When you create the object that contains the loop, you pass the objects you created in your example code (or rather references to them) to that object. It's possible either to pass them via an appropriate constructor or via setter methods to mention the most common examples. How you do this in you main loop strongly depends on your class design, but you'd be able to perform the rendering something like this
The adventages of this approach are that you eliminate the need for global objects or singletons and avoid that your objects know how to create your device objects. The only thing your objects have to know is that there are some device abstractions they can use. They do not have to know anything else.
This approach is called Dependency Injection and is considered to be a very important pattern of modern OO-design.