Open panzerdp opened 4 years ago
Hmm, interesting. Let me see if I understand this correctly - you're looking to do something like:
const cmdHandler1 = container.resolve('CommandHandlerDecorated1'); // returns the moral equivalent of new CommandHandlerDecorator(container.resolve('CommandHandlerIml1'))
const cmdHandler2 = container.resolve('CommandHandlerDecorated2'); // returns the moral equivalent of new CommandHandlerDecorator(container.resolve('CommandHandlerIml2'))
Unfortunately, there's no way to pass a discriminator into the resolution of CommandHandlerDecorator
which would allow it to wrap the correct CommandHandler
.
I think you can do this with a factory, but probably not through regular resolution. Could you write an example of your ideal calling pattern if we were to implement registerDecorator()
?
Let me see if I understand this correctly - you're looking to do something like
Yes, I'm looking for a way to resolve decorated implementations.
I think you can do this with a factory, but probably not through regular resolution. Could you write an example of your ideal calling pattern if we were to implement registerDecorator()?
Here's a possibility:
container.register<CommandHandler>(CommandHandlerImpl1, CommandHandlerImpl1);
container.register<CommandHandler>(CommandHandlerImpl2, CommandHandlerImpl2);
container.register<CommandHandler>(CommandHandlerDecorator, CommandHandlerDecorator);
container.registerDecorator(
CommandHandlerImpl1, // the decoratee
CommandHandlerDecorator, // the decorator
'CommandHandler' // the name of the prop inside the decorator
);
container.registerDecorator(
CommandHandlerImpl2, // the decoratee
CommandHandlerDecorator, // the decorator
'CommandHandler' // the name of the prop inside the decorator
);
const cmdHandler1 = container.resolve(CommandHandlerImpl1);
const cmdHandler2 = container.resolve(CommandHandlerImpl2);
Please take a look here how decorators are registered in SimpleInjector (C#).
How are those cmdHandlers used? If they are resolved as constructor params elsewhere, there is a way to do it (I think), by using the @injectWithTransform()
decorator. So you might be able to do something like
@injectable()
class CommandUser {
constructor(@injectWithTransform(CommandHandlerImpl1, CommandHandlerTransform) decoratedCommandHandler: CommandHandler){
// ...
}
}
Where you'd define the CommandHandlerTransform
as something like:
class CommandHanderTransform implements Transform <CommandHandler, CommandHandler>{
public transform(cmdHandler: CommandHandler): CommandHandler {
return new CommandHandlerDecorator(cmdHandler);
}
}
This won't work as written if CommandHandlerDecorator
itself has dependencies that need to be resolved from the container. In that case, you would need to make CommandHandlerDecorator
@autoInjectable()
so that you could pass the original cmdHandler into it and then have the dependencies resolved.
Another possibility is to add a new feature to extend the transform behavior on explicit resolution, where we could have something like container.resolveWithTransform<TIn, TOut>(<TIn>, Transform<TIn, Tout>): TOut
, which would use the existing Transform behavior.
How are those cmdHandlers used?
1) I can either use them directly into a component:
function MyComponent() {
const handle = () => {
const myCommand = container.resolve(CommandHandlerImpl1);
myCommand.execute();
};
return <button onClick={handle}></button>
}
2) Or supply as dependencies to other service:
@injectable()
class MyService {
constructor(private cmd1: CommandHandlerImpl1, private cmd2: CommandHandlerImpl2) {}
doSomething(flag) {
if (flag) {
this.cmd1.execute();
} else {
this.cmd2.execute();
}
}
}
That's why I'd like to register the decorator on registration phase, and during regular resolving of CommandHandlerImpl1
and CommandHandlerImpl2
the DI should automatically decorate these dependencies with CommandHandlerDecorator
.
Also I'd like to register multiple decorators on top of the same implementation:
container.registerDecorator(
CommandHandlerImpl1, // the decoratee
CommandHandlerDecorator1, // the first decorator
'CommandHandler' // the name of the prop inside the decorator
);
container.registerDecorator(
CommandHandlerImpl1, // the decoratee
CommandHandlerDecorator2, // the second decorator
'CommandHandler' // the name of the prop inside the decorator
);
// ...
This won't work as written if CommandHandlerDecorator itself has dependencies that need to be resolved from the container.
Yes, I'd like the decorator also to have its dependencies resolved by the container.
The transform solution isn't quite what I'm looking for, since it clutters the resolving of dependencies.
@panzerdp did you ever find a solution to decorator pattern with DI containers in Javascript? I've looked at 5 different DI containers now, and all but one don't support it... and the one that does support it has perf issues once you register more than 20 dependencies.
@AlexErrant I just did it manually. :)
Hello! Thank you for such a wonderful library.
To leverage the decorator pattern, I need to inject different Implementations of the same Interface into the Decorator.
For example.
1) The service interface:
2) Multiple services implementations:
3) The decorator class:
Using Pure DI, I can decorate and instantiate the 2 implementations as such:
But how can it be implemented using tsyringe?
My guess is that tsyringe needs a container.registerDecorator() (like Simple Injector does) to handle situations like these.
Thank you.