Подтвердить что ты не робот

Наследование с использованием интерфейса + абстрактного класса. Хорошая практика?

Я не уверен, как назвать этот вопрос, но в основном у меня есть такой интерфейс:

public interface IFoo
{
    string ToCMD();
}

несколько классов абстракции, которые реализуют IFoo как:

public abstract class Foo : IFoo
{
   public abstract string ToCMD();
}

public abstract class Bar : IFoo
{
    public abstract string ToCMD();
}

тогда классы, наследующие Foo и Bar:

public class FooClass1 : Foo
{
    public override string ToCMD()
    {return "Test";}
} ///there are about 10 foo classes.

public class BarClass : Bar
{
    public override string ToCMD()
    {return "BarClass";}
} ///about the same for bar classes.

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

public class Store<T> : List<T>  where T : IFoo {}

Затем я могу ограничить типы, которые входят в него, но с помощью интерфейса он все равно будет использовать любой тип IFoo.

Что-то вроде:

Store<Foo> store = new Store<Foo>(); //Only Foo types will work.
store.Add(new FooClass1()); //Will compile.

Store<IFoo> store = new Store<IFoo>(); //All IFoo types will work.
store.Add(new FooClass1()); //Will compile.
store.Add(new BarClass()); //Will compile.

Мой вопрос: хорошо ли это для этого? или есть лучший способ?

ИЗМЕНИТЬ: Изображение- > alt text

4b9b3361

Ответ 1

Потребность в цепочке наследования в целом сомнительна.

Однако конкретный сценарий объединения абстрактного базового класса с интерфейсом. Я вижу это так:

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

Тем не менее, если я пишу библиотеку, и это часть моего API/Framework, я обычно буду включать "реализацию по умолчанию", которая может использоваться как базовый класс. Он будет реализовывать методы интерфейса наивным, общим способом, где это возможно, и оставлять остальные для наследователей для реализации/переопределения по мере необходимости.

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

Вкратце, интерфейс более ценен, чем базовый класс, но базовый класс может сэкономить много времени и сократить возможности реализации багги-интерфейса.

Ответ 2

Хорошо, это то, для чего используется ключевое слово where. Вероятно, вам необходимо оценить вашу объектную модель, чтобы убедиться, что необходима глубина вашего наследования. Глубокие иерархии наследования, как правило, усложняют проекты и, как правило, представляют собой красный флаг, но это зависит от ситуации.

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

Короткий ответ: убедитесь, что вы не наследуете безумие.

Надеюсь, что это поможет!

Ответ 3

Вы также можете использовать интерфейсы. Нет смысла использовать абстрактный класс.

public interface Foo : IFoo
{
}

 public interface  Bar : IFoo
{
}

Ответ 4

вам нужен интерфейс; вы можете или не нуждаться в абстрактном классе.

в общем случае, если вы можете предоставить некоторое полезное поведение в базовом классе, тогда укажите базовый класс; если базовый класс не является полным по себе, тогда сделайте его абстрактным (MustInherit в языке VB)

в противном случае интерфейс достаточно

Ответ 5

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

abstract class Base
{
    public abstract string ToCMD();
}

abstract class Foo : Base { }

abstract class Bar : Base { }

Надеемся, у вас есть другие участники классов Foo и Bar! Это позволит вам либо ограничить вашу пользовательскую коллекцию с помощью Base, либо просто использовать простой List<Base>.