Я всегда борюсь с оформлением + интерфейсами.Допустим, у меня есть следующие интерфейсы «поведения»:
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 ().
- Это может привести к путанице / нежелательному поведению, если использовать метод из IMainComponent после вызова GetAs (что-то вроде ((IMainComponent) GetAs ()). DoSomethingB (); ==> это может вызвать реализациюзавернутый объект, а не полное оформление.
- Необходимо вызвать метод GetAs (), и выход из кода с приведением / обычным "As" не будет работать.
Как вамразрешить / разрешить эту проблему? Существует ли шаблон для решения этой проблемы?
PD: мой вопрос касается окончательной реализации C #, но, возможно, решение более широкое.