我一直在努力装饰+接口。说我有以下“行为”界面:
interface IFlyable { void Fly();}
interface ISwimmable { void Swim();}
主界面
interface IMainComponent { void DoSomethingA(); void DoSomethingB();}
主界面上的装饰器
public class Decorator : IMainComponent
{
private readonly IMainComponent decorated;
[..]
public virtual void DoSomethingA()
{
decorated.DoSomethingA();
}
public virtual void DoSomethingB()
{
decorated.DoSomethingB();
}
}
我的问题是如何将装饰对象实现的所有接口转发给装饰器。一种解决方案是使装饰器实现接口:
public class Decorator : IMainComponent, IFlyable, ISwimmable
{
[..]
public virtual void Fly()
{
((IFlyable)decorated).Fly();
}
public virtual void Swim()
{
((ISwimmable)decorated).Swim();
}
但是我不喜欢它,因为:
另一种解决方案是添加“手动投射”以传播装饰树:
public class Decorator : IMainComponent
{
public T GetAs<T>()
where T : class
{
//1. Am I a T ?
if (this is T)
{
return (T)this;
}
//2. Maybe am I a Decorator and thus I can try to resolve to be a T
if (decorated is Decorator)
{
return ((Decorator)decorated).GetAs<T>();
}
//3. Last chance
return this.decorated as T;
}
但是问题是:
您如何解决/解决此问题?是否有解决此问题的模式?
PD:我的问题是最终的C#实现,但解决方案可能更广泛。
答案 0 :(得分:3)
您将需要为每个接口创建单独的装饰器。一种替代方法是为您的服务和通用装饰器使用通用接口。例如:
public interface ICommandService<TCommand>
{
Task Execute(TCommand command);
}
public class LoggingCommandService<TCommand> : ICommandService<TCommand>
{
public LoggingCommandService(ICommandService<TCommand> command, ILogger logger)
{
...
}
public async Task Execute(TCommand command)
{
// log
await this.command.Execute(command);
// log
}
}
答案 1 :(得分:1)
我认为您正在转向服务定位器模式-这是一个严重的反模式。您有一个服务定位器,如果您依赖一个功能如愿的界面:您可以要求任何东西。我认为这正是您的GetAs
带您去的地方。
服务定位器被认为是反模式,因为它隐藏了类所具有的依赖关系。相反,您仅将服务定位器视为单个依赖项,但没有立即看到将要调用的依赖项。
如果您要求实现,我建议使用依赖项注入框架。市场上有很多它们,例如MEF,Ninject,Unity,Windsor,DryIoC,SimpleInject,LightInjector,Grace,Stashbox,...仅是我想起的几个。
装饰器的要点完全不同。如果您不仅转发接口调用,还向它添加一些额外的逻辑(例如重试行为),则使用装饰器。但是,在那种情况下,您仍然将自己局限于原始接口的方法。
答案 2 :(得分:0)
装饰器模式不适用于向装饰对象添加新方法。这就是您要尝试做的事情,而且不可能用静态类型的语言优雅地做。
Decorator有用的地方是装饰器和装饰组件的接口相同,并且装饰器在将请求沿着装饰器链传递之前或之后都向该方法添加了一些额外的功能。