我一直在为装饰器和接口而苦恼。假设我有以下“行为”接口:
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;
}
但问题在于:
- 调用者可以在调用GetAs()后操纵包装对象。
- 如果在GetAs()之后使用IMainComponent的方法(例如((IMainComponent)GetAs()).DoSomethingB();),这可能会导致混淆/不希望的行为,因为这可能调用包装对象的实现,而不是完整的装饰。
- 必须调用GetAs()方法,使用转换/常规"As"退出代码将无法工作。
注:我的问题是针对C#最终实现的,但解决方案可能更加广泛。
decorated
的数据类型是什么?它在哪里声明的?你的问题不太清楚。而且 decorator 和 decorated 之间存在混淆。 - ChetanDecorator: MainComponent { override Fly... }
。 - Renatas M.