2016-12-13 3 views
-1

Я пытаюсь выяснить простой способ компоновки интерфейсов без создания нового суб-интерфейса, поскольку интерфейсы имеют многомерные отношения.Как создавать интерфейсы без использования суб-интерфейса или явного класса

interface IAnimal {} 

interface ISwimmingAnimal : IAnimal {} 

interface IVertebrate : IAnimal {} 

interface IMammal : IVertebrate 
{ 
    IMammal Process(); 
} 

interface IInvertebrate : IAnimal {} 

interface IMollusc : IInvertebrate 
{ 
    IMollusc Process(object data); 
} 

Таким образом, некоторые конкретные классы были бы

class Vertebrate : IVertebrate {} 

class SwimmingInvertebrate : ISwimmingAnimal, IInvertebrate {} 

class SwimmingMammal : ISwimmingAnimal, IMammal 
{ 
    IMammal Process(); 
} 

без необходимости композиции интерфейса, таких как ISwimmingAnimalInvertebrate или ISwimmingAnimalMammal.

Все хорошо, пока я не нужно использовать класс/интерфейс, который будет содержать экземпляры некоторых compostioned типов (То, что я хочу в теории):

interface IAnimalsCollection 
{ 
    IEnumerable<IAnimal> Items { get; } //Aggregate collection of Vertebrate and Invertebrates 
    {IAnimal, IVertebrate} Vertebrate { get; } 
    IEnumerable<{IAnimal, IInvertebrate}> Invertebrates { get; } 

    void UpdateVertebrate({IAnimal, IVertebrate} vertebrate); 
    void UpdateInvertebrate({IAnimal, IInvertebrate} invertebrate, int index); 
} 

class SwimmingAnimalsCollection : IAnimalsCollection 
{ 
    public IEnumerable<ISwimmingAnimal> Items { get; } //Aggregate collection of Vertebrate and Invertebrates 
    public {ISwimmingAnimal, IMammal} Vertebrate { get; } 
    public IEnumerable<{ISwimmingAnimal, IMollusc}> Invertebrates { get; } 

    public void UpdateVertebrate({ISwimmingAnimal, IMammal} vertebrate); 
    public void UpdateInvertebrate({ISwimmingAnimal, IMollusc} invertebrate, int index); 
} 

Я попытался это

interface IAnimalsCollection<TAnimal, TVertebrate, TInvertebrate> 
    where TAnimal : IAnimal 
    where TVertebrate : TAnimal, IVertebrate 
    where TInvertebrate : TAnimal, IInvertebrate 
{ 
    IEnumerable<IAnimal> Items { get; } //Aggregate collection of Vertebrate and Invertebrates 
    {IAnimal, IVertebrate} Vertebrate { get; } 
    IEnumerable<{IAnimal, IInvertebrate}> Invertebrates { get; } 

    void UpdateVertebrate(TVertebrate vertebrate); 
    void UpdateInvertebrate(TInvertebrate invertebrate, int index); 
} 

class SwimmingAnimalsCollection<TAnimal, TVertebrate, TInvertebrate> : AnimalsCollection<TAnimal, TVertebrate, TInvertebrate> 
    where TAnimal : ISwimmingAnimal 
    where TVertebrate : TAnimal, IMammal 
    where TInvertebrate : TAnimal, IMollusc 
{ 
    public IEnumerable<TAnimal> Items { get; } //Aggregate collection of Vertebrate and Invertebrates 
    public TVertebrate Vertebrate { get; } 
    public IEnumerable<TInvertebrate> Invertebrates { get; } 

    public void UpdateVertebrate(TVertebrate vertebrate) {} 
    public void UpdateInvertebrate(TInvertebrate invertebrate, int index) {} 
} 

За исключением того, что я не могу найти способ создать экземпляр SwimmingAnimalsCollection с помощью только интерфейсов, так как это была проблема, которую я пытался решить в первую очередь, и я не могу использовать реализации классов из-за IMammal и IMollusc, потенциально возвращающих весь в разных классах. Фактически, в реальной реализации есть пары классов, которые являются UnProcessedX, которые возвратят класс ProcessedX, который просто вернется, если вы попытаетесь обработать его дальше.

Так как я хотел бы сделать

collection.UpdateVertebrate((TVertebrate)collection.Vertebrate.Process()) 

и ProcessedVertebrate не является UnProssesedVertebrate.

я вижу примерно три пути решения этого:

  • Я мог бы сломаться и создавать композиционные интерфейсы, убивающий любую простую инъекцию зависимостей .
  • Я мог бы создать фиктивные абстрактные базовые классы (и надеюсь, что ничто не нуждается в цепочке глубокого наследования ), убивая любую возможность зависимости инъекции.
  • Или я мог бы использовать компилятор C# с волшебной палочкой до тех пор, пока он не смягчится и не позволит создать композицию на месте.
+1

Ключевое слово уже в вашем вопрос. Состав. Не наследование. https: //en.wikipedia.org/wiki/Composition_over_inheritance – CodeCaster

+0

@CodeCaster Тогда я не уверен, что правильно объяснил. Мне не нужен класс, который может выполнять A, B и C, мне нужен класс, который может содержать экземпляры {IA, IB} и {IA IC} без определения промежуточных интерфейсов IAB и IAC. – Tory

+0

Но все 'IB' и' IC' в вашем примере 'IA', почему вам нужно ограничивать оба? – InBetween

ответ

1

Я думаю, что ближайший вы можете получить то, что вы хотите, это следующие настройки:

public interface IAnimal { } 
public interface IVertebrate<out TAnimal>: IAnimal where TAnimal : IAnimal { } 
public interface IInvertebrate<out TAnimal>: IAnimal where TAnimal : IAnimal { } 
public interface ISwimmingAnimal : IAnimal { } 
public interface ISwimmingVertebrate : IVertebrate<ISwimmingAnimal> { } 
public interface ISwimmingInvertebrate : IInvertebrate<ISwimmingAnimal> { } 

ли уведомление, что вы по-прежнему создавая dependeny между интерфейсами. Теперь ваша коллекция будет выглядеть следующим образом:

public interface IAnimalsCollection<TAnimal, TVertebrate, TInvertebrate> 
    where TAnimal: IAnimal 
    where TVertebrate: IVertebrate<TAnimal> 
    where TInvertebrate: IInvertebrate<TAnimal> 
{ 
    IEnumerable<IAnimal> Animals { get; } 
    IEnumerable<TVertebrate> Vertebrates { get; } 
    IEnumerable<TInvertebrate> Invertebrates { get; } 
    .... 
} 

public class AnimalsCollection<TAnimal, TVertebrate, TInvertebrate> : IAnimalsCollection<TAnimal, TVertebrate, TInvertebrate> 
    where TAnimal : IAnimal 
    where TVertebrate : IVertebrate<TAnimal> 
    where TInvertebrate : IInvertebrate<TAnimal> 
{ 
    public AnimalsCollection(IEnumerable<TVertebrate> vertebrates, IEnumerable<TInvertebrate> invertebrates) 
    { 
     Vertebrates = vertebrates; 
     Invertebrates = invertebrates; 
    } 

    public IEnumerable<IAnimal> Animals => Vertebrates.Cast<IAnimal>().Concat(Invertebrates.Cast<IAnimal>());  
    public IEnumerable<TInvertebrate> Invertebrates { get; } 
    public IEnumerable<TVertebrate> Vertebrates { get; } 
} 

Некоторые конкретные типы:

public abstract class SwimmingAnimal: ISwimmingAnimal { } 
public class JellyFish: IInvertebrate<SwimmingAnimal> { } 
public class Fish: IVertebrate<SwimmingAnimal> { } 

И теперь вы можете создать экземпляр конкретный сбор только с помощью интерфейса:

var swimmingCollection = new AnimalsCollection<ISwimmingAnimal, IVertebrate<ISwimmingAnimal>, IInvertebrate<ISwimmingAnimal>>(Enumerable.Empty<Fish>(), Enumerable.Empty<JellyFish>()); 
+0

К сожалению, я получаю недопустимое исключение листинга при доступе к Животные с непустой коллекцией (Невозможно наложить объект типа «Рыба» на тип «ISwimmingAnimal».). И я пробовал слияние вашего предлагаемого кода и моего (где телеребрат: TAnimal, IVertebrate ). В любом случае, это то, чего я пытался избежать - создание промежуточной компоновки Interfaces. – Tory

+0

@Tory woops, сделал ошибку. 'Животные 'должны возвращать' IEnumerable ', не имеет смысла возвращать' IEnumerable ', потому что вы не можете сделать интерфейс расширенным универсальным типом; 'IVertebrate' и' IInvertebrate' должны реализовывать 'IAnimal'. Исправлен код. – InBetween

+0

Да, это исправлено (и по-прежнему допустимо по моим требованиям). И это приблизило кодекс к моей «объединенной» версии - но о намного проще. Я уже проверил и расширил его, скажем, «IMammal : IVertebrate где TAnimal: IAnimal' по-прежнему работает с' var animals = new AnimalsCollection , IInvertebrate > (...) '. – Tory

Смежные вопросы