Наследование интерфейса против класса, реализующего два интерфейса

#c# #oop #inheritance #interface

#c# #ооп #наследование #интерфейс

Вопрос:

Допустим, у нас есть два интерфейса:

 interface Ilayer1 : Ilayer2
{
  string testMethod1();
}


interface Ilayer2
{
   string testMethod2();
}

class A: Ilayer1
{
   ...//implement all methods of Ilayer 1 and Ilayer2
}
  

Итак, мои вопросы:

  1. Какова цель наследования интерфейса? Поскольку они могут иметь разные сигнатуры методов для реализации, так что же Ilayer1 получается при наследовании от Ilayer2 ?
  2. Мы можем просто удалить отношение наследования между Ilayer1 и Ilayer2 и просто позволить классу A реализовать оба интерфейса как class A: Ilayer1, Ilayer2

Комментарии:

1. IPrimate : IMammal : IVertebrate например. Каждый из них добавляет определенные характеристики к тому, что он наследует, чего не могут другие. Например, вы также могли бы иметь IFish : IVertebrate поскольку рыбы и млекопитающие обладают некоторыми отличными друг от друга свойствами.

2. Итак, вы согласны с повторением снова и снова Ilayer2 в каждом разработчике Ilayer1 ?

3. Если ILayer1 добавляет функциональность к ILayer2 или ILayer2 является чем-то, что ILayer1 также используется (например IComparable ), тогда это имеет смысл. Примеры из реального мира сделали бы это более очевидным.

Ответ №1:

Это та же семантика, что и наследование между классами.
В мире ООП производный тип наследует базовый тип, когда это более конкретная версия базового типа — является ли этот тип интерфейсом или классом, принцип остается тем же.

Вот пример из .Net framework: IList Интерфейс наследует интерфейс ICollection, который наследует интерфейс IEnumerable.

IEnumerable Интерфейс предоставляет GetEnumerator() метод, который необходим для перечисления с использованием foreach цикла.

ICollection Добавлены новые возможности: Count свойство и CopyTo метод.

IList Добавляет еще больше возможностей — индексатор, методы Add и Remove и так далее.

Таким образом, IList это более специфический тип an ICollection , который является более специфическим типом an IEnumerable .

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

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

Что касается вопроса о том, почему бы не удалить наследование и реализовать отдельно два интерфейса — это имело бы смысл, если бы интерфейсы не были связаны — как, например, в Control классе (в System.Windows.Формы) — Он реализует множество интерфейсов, таких как IDropTarget и IComponent , которые не связаны между собой.

Ответ №2:

Один реальный пример:

 interface IShape 
{
    double X { get; }
    double Y { get; }
}

interface IAreaShape : IShape
{
    double GetArea();
}

interface IPerimeterShape : IShape
{
    double GetPerimeter();
}
  

Теперь предположим, что у вас есть Rectangle : IAreaShape, IPerimeterShape фигура, и вы можете вычислить как ее площадь, так и периметр. Когда у вас есть объект типа IAreaShape or IPerimeterShape , он должен быть IShape . Это ответ на ваш 2-й вопрос.

Теперь для вашего первого вопроса, просто предположим, для примера, что у нас есть Circle , что я могу вычислить только площадь, но не периметр. В этом случае мы просто объявляем это Circle : IAreaShape .

И для управления: List<IShape> shapes может принимать как круг, так и прямоугольник и что угодно, если они реализуют IShape (или любой производный тип IShape ). Вы можете сделать это, если вам все равно, можно ли вычислить их площадь или периметр, но вы всегда можете получить их координаты X и Y.

Ответ №3:

  1. Ilayer1 наследуется, Ilayer2 так что это означает, что любому реализующему Ilayer1 нужно будет реализовать testMethod1() и testMethod2() . Зачем это нужно? Почему бы и нет? Потому что мы можем? Я думаю, что в больших проектах с большим количеством интерфейсов это может стать довольно утомительным, если вам нужно указать все интерфейсы, которые какой-либо класс реализует отдельно (хотя в хорошем SOLID дизайне класс обычно в любом случае не должен реализовывать более нескольких интерфейсов). Таким образом, вы можете «группировать» интерфейсы, заставляя их реализовывать другие. Вы также можете «модифицировать» интерфейс с помощью наследования интерфейса.
  2. Вы можете. Это зависит от вас. Хорошо иметь выбор. Используйте наследование или реализуйте интерфейсы по отдельности. Это зависит от вас.

Интерфейсы могут наследоваться от других интерфейсов. Класс может включать интерфейс несколько раз через базовые классы, которые он наследует, или через интерфейсы, которые наследуют другие интерфейсы. Однако класс может предоставить реализацию интерфейса только один раз и только в том случае, если класс объявляет интерфейс как часть определения класса ( class ClassName : InterfaceName ). Если интерфейс унаследован из-за того, что вы унаследовали базовый класс, который реализует интерфейс, базовый класс обеспечивает реализацию элементов интерфейса. Однако производный класс может переопределять любые элементы виртуального интерфейса вместо использования унаследованной реализации. Источник