【发布时间】:2019-03-14 15:20:51
【问题描述】:
我总是在装饰 + 界面上苦苦挣扎。假设我有以下“行为”界面:
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”的退出代码将不起作用。
您如何处理/解决此问题?有解决这个问题的模式吗?
PD:我的问题是关于最终的 C# 实现,但也许解决方案更广泛。
【问题讨论】:
-
你想达到什么目的?有没有现实生活中的例子?
-
这里
decorated的数据类型是什么?它在哪里声明?目前尚不清楚您在问什么。并且装饰器和装饰器之间存在混淆...... -
为什么要在装饰器中实现接口? MainComponent 应该实现所有接口并且 Decorator 扩展 MainComponent 对象。所以
Decorator: MainComponent { override Fly... } -
@devdigital :我多次遇到这个问题。最新的现实生活是创建一个在不同触发器(ITimedComponent、IFileChangedComponent、IFrequencyComponent 等)上完成其工作的组件。装饰用于在执行作业时记录/在之前/之后存储指标,在作业运行之前执行数据冻结等。
-
@ChetanRanpariya 装饰是一个 IMainComponent 实例。 sn-p没满,我修改了。
标签: c# design-patterns