oop - strong - type theory




Интерфейс vs Базовый класс (20)

Когда следует использовать интерфейс и когда я должен использовать базовый класс?

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

Если у меня есть класс для собак и кошек. Почему я хочу реализовать IPet вместо PetBase? Я могу понять, есть ли интерфейсы для ISheds или IBarks (IMakesNoise?), Потому что они могут быть размещены на домашнем животном по принципу домашнего животного, но я не понимаю, что использовать для общего Pet.


Интерфейсы

  • Определяет контракт между двумя модулями. Не может быть никакой реализации.
  • Большинство языков позволяют реализовать несколько интерфейсов
  • Изменение интерфейса - это нарушение. Все реализации должны быть перекомпилированы / изменены.
  • Все участники являются общедоступными. Реализации должны внедрять всех членов.
  • Интерфейсы помогают в развязывании. Вы можете использовать макетные фреймворки для издевательства над интерфейсом
  • Интерфейсы обычно указывают на поведение
  • Интерфейсные реализации развязаны / изолированы друг от друга

Базовые классы

  • Позволяет добавить некоторую стандартную реализацию, которую вы получаете бесплатно путем вывода
  • Кроме C ++, вы можете получить только один класс. Даже если бы это было из нескольких классов, это, как правило, плохая идея.
  • Изменение базового класса относительно просто. Деривациям не нужно ничего делать
  • Базовые классы могут объявлять защищенные и общедоступные функции, к которым можно получить доступ посредством дериваций
  • Абстрактные базовые классы не могут легко насмехаться, как интерфейсы
  • Базовые классы обычно указывают иерархию типов (IS A)
  • Отличия классов могут зависеть от некоторого базового поведения (иметь сложные знания о реализации родителя). Вещи могут быть беспорядочными, если вы внесете изменения в базовую реализацию для одного парня и сломаете других.

В общем, вы должны поддерживать интерфейсы над абстрактными классами. Одной из причин использования абстрактного класса является то, что у вас есть общая реализация среди конкретных классов. Конечно, вы все равно должны объявить интерфейс (IPet) и иметь абстрактный класс (PetBase), реализующий этот интерфейс. Используя небольшие, четкие интерфейсы, вы можете использовать множественные числа для дальнейшего повышения гибкости. Интерфейсы обеспечивают максимальную гибкость и переносимость типов через границы. При прохождении ссылок через границы всегда передавайте интерфейс, а не конкретный тип. Это позволяет принимающей стороне определить конкретную реализацию и обеспечивает максимальную гибкость. Это абсолютно верно при программировании в режиме TDD / BDD.

«Банда четырех» заявила в своей книге «Поскольку наследование предоставляет подкласс для подробностей реализации его родителя, часто говорят, что« наследование прерывает инкапсуляцию ». Я верю, что это правда.


Вот основные и простые определения интерфейса и базового класса:

  • Базовый класс = наследование объектов.
  • Интерфейс = функциональное наследование.

ура


Давайте рассмотрим ваш пример класса Dog и Cat, и давайте проиллюстрируем использование C #:

И собака, и кошка - это животные, в частности четвероногие млекопитающие (животные слишком общие). Предположим, что у вас есть абстрактный класс Mammal, для обоих из них:

public abstract class Mammal

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

  • Кормить
  • Приятель

Все это поведение, которое имеет более или менее ту же реализацию между любыми видами. Чтобы определить это, вы будете иметь:

public class Dog : Mammal
public class Cat : Mammal

Предположим теперь, что есть другие млекопитающие, которые мы обычно видим в зоопарке:

public class Giraffe : Mammal
public class Rhinoceros : Mammal
public class Hippopotamus : Mammal

Это все равно будет иметь силу, поскольку в основе функциональности Feed() и Mate() все равно будет то же самое.

Однако, жирафы, носороги и бегемоты - это не совсем животные, из которых вы можете сделать домашних животных. Вот где интерфейс будет полезен:

public interface IPettable
{
    IList<Trick> Tricks{get; set;}
    void Bathe();
    void Train(Trick t);
}

Реализация вышеуказанного контракта не будет одинаковой между кошкой и собакой; их реализация в абстрактном классе для наследования будет плохой идеей.

Теперь определения вашей собаки и кошки должны выглядеть так:

public class Dog : Mammal, IPettable
public class Cat : Mammal, IPettable

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

Следовательно, поскольку вы обычно можете наследовать только один абстрактный класс (в большинстве статически типизированных языков OO, которые являются ... исключениями включают C ++), но иметь возможность реализовать несколько интерфейсов, он позволяет строить объекты строго в соответствии с требованиями .


Интерфейсы и базовые классы представляют две разные формы отношений.

Наследование (базовые классы) представляет собой отношение «is-a». Например, собака или кошка - это «домашнее животное». Эта связь всегда представляет (единственную) цель класса (в сочетании с принципом «единой ответственности» ).

С другой стороны, интерфейсы представляют дополнительные функции класса. Я бы назвал это отношением «есть», как в « Foo is disposable», следовательно, IDisposable интерфейс в C #.


Ну, Джош Блох сказал сам в « Эффективной Java 2d :

Предпочитать интерфейсы над абстрактными классами

Некоторые основные моменты:

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

  • Интерфейсы идеально подходят для определения микшинов . Как ни странно, mixin - это тип, который класс может реализовать в дополнение к его «первичному типу», чтобы объявить, что он обеспечивает некоторое необязательное поведение. Например, Comparable - это интерфейс mixin, который позволяет классу объявлять, что его экземпляры упорядочены относительно других взаимно сопоставимых объектов.

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

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

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

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

PS: Купить книгу. Это намного более подробно.


Случай для базовых классов над интерфейсами был хорошо объяснен в Руководстве по субмастеру .NET:

Базовые классы и интерфейсы Тип интерфейса - это частичное описание значения, которое может поддерживаться многими типами объектов. По возможности используйте базовые классы вместо интерфейсов. С точки зрения управления версиями классы более гибкие, чем интерфейсы. С классом вы можете отправить Версия 1.0, а затем в Версии 2.0 добавить новый метод в класс. Пока метод не является абстрактным, любые существующие производные классы продолжают функционировать без изменений.

Поскольку интерфейсы не поддерживают наследование реализации, шаблон, применяемый к классам, не применяется к интерфейсам. Добавление метода к интерфейсу эквивалентно добавлению абстрактного метода в базовый класс; любой класс, реализующий интерфейс, сломается, потому что класс не реализует новый метод. Интерфейсы подходят в следующих ситуациях:

  1. Несколько несвязанных классов хотят поддерживать протокол.
  2. Эти классы уже установили базовые классы (например, некоторые элементы управления пользовательским интерфейсом (UI), а некоторые - веб-службы XML).
  3. Агрегация не подходит или практически невозможна. Во всех других ситуациях наследование классов является лучшей моделью.

Современный стиль - это определение IPet и PetBase.

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

Но базовые классы полезны для простых реализаций и общих утилит. Таким образом, предоставляем абстрактный базовый класс, чтобы сэкономить время и код.


У меня грубое правило большого пальца

Функциональность: может быть разной во всех частях: интерфейс.

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

Данные и функциональность, фактически работающие, если расширены только с небольшими изменениями: обычный (конкретный) класс

Данные и функциональность, никаких изменений не запланировано: обычный (конкретный) класс с окончательным модификатором.

Данные и, возможно, функциональность: только для чтения: перечисление членов.

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


Хуан,

Мне нравится думать о интерфейсах как способ характеризовать класс. Определенный класс породы собак, скажем, YorkshireTerrier, может быть потомком родительского класса собак, но он также реализует IFurry, IStubby и IYippieDog. Таким образом, класс определяет, что такое класс, но интерфейс сообщает нам об этом.

Преимущество этого заключается в том, что я могу, например, собрать все IYippieDog и выбросить их в свою коллекцию Ocean. Поэтому теперь я могу охватить определенный набор объектов и найти те, которые соответствуют критериям, на которые я смотрю, без слишком тщательного изучения класса.

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

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

Поэтому, если вы думаете, как я, вы определенно скажете, что Cat and Dog - это IPettable. Это характеристика, которая соответствует их обоим.

Другая часть этого, хотя, должна ли она иметь тот же базовый класс? Вопрос заключается в том, что их нужно рассматривать в целом как одно и то же. Конечно, они оба - Животные, но это соответствует тому, как мы будем использовать их вместе.

Скажем, я хочу собрать все классы Animal и поместить их в контейнер Ark.

Или они должны быть млекопитающими? Может, нам нужна какая-то крестовая доильная фабрика?

Нужно ли вообще связываться с ними? Достаточно ли просто знать, что они оба являются IPettable?

Я часто чувствую желание получить целую иерархию классов, когда мне действительно нужен только один класс. Я делаю это в ожидании, когда-нибудь мне это понадобится, и обычно я никогда этого не делаю. Даже когда я это делаю, я обычно нахожу, что я должен многое сделать, чтобы исправить это. Это потому, что первый класс, который я создаю, - это не Собака, мне не повезло, это вместо Платипа. Теперь вся моя иерархия классов основана на странном случае, и у меня много потерянного кода.

В какой-то момент вы также можете обнаружить, что не все кошки - это IPettable (например, этот голый). Теперь вы можете перенести этот интерфейс на все подходящие производные классы. Вы обнаружите, что гораздо менее разрушительное изменение, которое внезапно Кошки больше не производятся от PettableBase.


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

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

Поэтому вместо IPet или PetBase у вас может быть Pet с параметром IFurBehavior. Параметр IFurBehavior устанавливается методом CreateDog () для PetFactory. Именно этот параметр вызывается для метода shed ().

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

Я рекомендую этот шаблон даже в языках с множественным наследованием.


Источник : http://jasonroell.com/2014/12/09/interfaces-vs-abstract-classes-what-should-you-use/

C # - замечательный язык, который созрел и развился за последние 14 лет. Это замечательно для разработчиков, потому что зрелый язык предоставляет нам множество возможностей языка, которые в нашем распоряжении.

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

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

Все это хорошо и хорошо, и обеспечивает отличное повторное использование кода и придерживается принципа DRY (Do not Repeat Yourself) разработки программного обеспечения. Абстрактные классы отлично подходят, когда у вас есть отношения «есть».

Например: золотистый ретривер - это «тип собаки». Это пудель. Они оба могут лаять, как могут все собаки. Тем не менее, вы можете заявить, что парк пуделя значительно отличается от «собачьей коры» по умолчанию. Поэтому для вас может быть смысл реализовать что-то следующим образом:

public abstract class Dog
{
      public virtual void Bark()
      {
        Console.WriteLine("Base Class implementation of Bark");
      }
}

public class GoldenRetriever : Dog
{
   // the Bark method is inherited from the Dog class
}

public class Poodle : Dog
{
  // here we are overriding the base functionality of Bark with our new implementation
  // specific to the Poodle class
  public override void Bark()
  {
     Console.WriteLine("Poodle's implementation of Bark");
  }
}

// Add a list of dogs to a collection and call the bark method.

void Main()
{
    var poodle = new Poodle();
    var goldenRetriever = new GoldenRetriever();

    var dogs = new List<Dog>();
    dogs.Add(poodle);
    dogs.Add(goldenRetriever);

    foreach (var dog in dogs)
    {
       dog.Bark();
    }
}

// Output will be:
// Poodle's implementation of Bark
// Base Class implementation of Bark

// 

Как вы можете видеть, это отличный способ сохранить код DRY и позволить реализовать реализацию базового класса, когда любой из типов может просто полагаться на Bark по умолчанию, а не на реализацию специального случая. Классы, такие как GoldenRetriever, Boxer, Lab, могли бы наследовать «по умолчанию» (бас-класс) Bark бесплатно, только потому, что они реализуют абстрактный класс Dog.

Но я уверен, что вы уже это знали.

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

Что это значит? Ну, например: Человек не утка ... а утка - не человек. Достаточно очевидно. Тем не менее, как утка, так и человек имеют «способность» плавать (учитывая, что человек прошел свои уроки плавания в 1-м классе :)). Кроме того, поскольку утка не является человеком или наоборот, это не «реальность», а «способность», и мы можем использовать интерфейс, чтобы проиллюстрировать это:

// Create ISwimable interface
public interface ISwimable
{
      public void Swim();
}

// Have Human implement ISwimable Interface
public class Human : ISwimable

     public void Swim()
     {
        //Human's implementation of Swim
        Console.WriteLine("I'm a human swimming!");
     }

// Have Duck implement ISwimable interface
public class Duck: ISwimable
{
     public void Swim()
     {
          // Duck's implementation of Swim
          Console.WriteLine("Quack! Quack! I'm a Duck swimming!")
     }
}

//Now they can both be used in places where you just need an object that has the ability "to swim"

public void ShowHowYouSwim(ISwimable somethingThatCanSwim)
{
     somethingThatCanSwim.Swim();
}

public void Main()
{
      var human = new Human();
      var duck = new Duck();

      var listOfThingsThatCanSwim = new List<ISwimable>();

      listOfThingsThatCanSwim.Add(duck);
      listOfThingsThatCanSwim.Add(human);

      foreach (var something in listOfThingsThatCanSwim)
      {
           ShowHowYouSwim(something);
      }
}

 // So at runtime the correct implementation of something.Swim() will be called
 // Output:
 // Quack! Quack! I'm a Duck swimming!
 // I'm a human swimming!

Использование таких интерфейсов, как приведенный выше код, позволит вам передать объект в метод, который «способен» что-то сделать. Коду все равно, как он это делает ... Все, что он знает, это то, что он может вызывать метод Swim для этого объекта, и этот объект будет знать, какое поведение следует выполнять во время выполнения на основе его типа.

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

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

Резюме:

Таким образом, мое основное правило - использовать абстрактный класс, если вы хотите реализовать функциональность по умолчанию для иерархии классов или / и классов или типов, с которыми вы работаете, совместно использовать отношения «есть» (например, пудель) «Тип собаки».

С другой стороны, используйте интерфейс, когда у вас нет отношения «есть», но есть типы, которые разделяют «способность» что-то делать или что-то (например, «Утка» не «человек». Однако утка и человеческая доля «Способность» плавать).

Еще одно отличие между абстрактными классами и интерфейсами заключается в том, что класс может реализовывать один-много интерфейсов, но класс может наследовать только от ОДНОГО абстрактного класса (или любого класса в этом отношении). Да, вы можете встраивать классы и иметь иерархию наследования (что многие программы делают и должны иметь), но вы не можете наследовать два класса в одном определении производного класса (это правило применяется к C #. На некоторых других языках вы можете это сделать, обычно только из-за отсутствия интерфейсов на этих языках).

Также помните, когда используете интерфейсы для соблюдения принципа разделения интерфейса (ISP). ISP утверждает, что ни один клиент не должен зависеть от методов, которые он не использует. По этой причине интерфейсы должны быть сфокусированы на конкретных задачах и обычно очень малы (например, IDisposable, IComparable).

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

Надеюсь, что это очистит для некоторых людей!

Также, если вы можете придумать какие-либо примеры или хотите что-то указать, пожалуйста, сделайте это в комментариях ниже!


@Joel: Некоторые языки (например, C ++) допускают множественное наследование.


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

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

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

Поведение должно сначала назначаться интерфейсам (включая интерфейс по умолчанию для класса) и продвигаться к базовому классу, только если они (а) являются общими для большой группы классов, которые являются подмножествами более крупного класса, - в том же смысле, что и «кошка» и «человек» являются подмножествами «млекопитающих».

Уловка, после того, как вы понимаете объектно-ориентированный дизайн достаточно лучше, чем я делал сначала, вы обычно делаете это автоматически, даже не задумываясь об этом. Таким образом, голая правда выражения «код для интерфейса, а не абстрактный класс» становится настолько очевидной, что вам трудно поверить, что кто-то это захочет сказать - и начните читать другие значения.

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


Другой вариант иметь в виду использование отношения «has-a», иначе «реализовано в терминах» или «композиции». Иногда это более чистый, более гибкий способ структурирования вещей, чем использование «is-a» наследования.

Возможно, логически не так логично сказать, что у собаки и кошки есть «есть» домашнее животное, но это позволяет избежать общих кратных ошибок наследования:

public class Pet
{
    void Bathe();
    void Train(Trick t);
}

public class Dog
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

public class Cat
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

Да, этот пример показывает, что существует много дублирования кода и отсутствие элегантности, участвующих в этом. Но стоит также признаться, что это помогает удержать собаку и кошку, отделяемую от класса Pet (в том, что «Собака и кошка» не имеют доступа к частным членам Pet), и она оставляет место для собак и кошек, чтобы наследовать что-то еще, - возможно, класс Млекопитающих.

Композиция предпочтительнее, когда не требуется никакого частного доступа, и вам не нужно ссылаться на Dog и Cat, используя общие рекомендации / указатели Pet. Интерфейсы дают вам общую справочную возможность и могут помочь сократить объем текста вашего кода, но они также могут запутывать вещи, когда они плохо организованы. Наследование полезно, когда вам нужен доступ к частному члену, и при его использовании вы обязуетесь очень привязывать свои классы для собак и кошек к вашему классу Pet, что является высокой стоимостью оплаты.

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


Концептуально интерфейс используется для формального и полуформального определения набора методов, которые предоставляет объект. Формально означает набор имен методов и подписей, полуформально означает человекочитаемую документацию, связанную с этими методами. Интерфейсы только описание к API ( в конце концов, API означает Application Programmer Interface ), они не могут содержать любую реализацию, и это не представляется возможным использовать или запустить интерфейс. Они только четко выражают договор о том, как вы должны взаимодействовать с объектом.

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

Существует различие между базовым классом и абстрактными базовыми классами (ABC). Интерфейс ABCs и реализация вместе. Аннотация за пределами компьютерного программирования означает «резюме», то есть «абстрактный == интерфейс». Абстрактный базовый класс может затем описать как интерфейс, так и пустую, частичную или полную реализацию, предназначенную для наследования.

Мнения о том, когда использовать интерфейсы в сравнении с абстрактными базовыми классами, и просто классы, будут сильно отличаться на основе как того, что вы разрабатываете, так и того, на каком языке вы разрабатываете. Интерфейсы часто связаны только со статически типизированными языками, такими как Java или C #, но динамически типизированные языки могут также иметь интерфейсы и абстрактные базовые классы. Например, в Python существует различие между классом, который заявляет, что он реализует интерфейс, и объект, который является экземпляром класса, и, как говорят, предоставляет этот интерфейс. На динамическом языке возможно, что два объекта, которые являются одновременно экземплярами одного и того же класса, могут заявить, что они предоставляют совершенно разныеинтерфейсы. В Python это возможно только для атрибутов объекта, тогда как методы - это общее состояние между всеми объектами класса. Однако в Ruby объекты могут иметь методы для каждого экземпляра, поэтому возможно, что интерфейс между двумя объектами одного и того же класса может варьироваться в зависимости от желаемого программиста (однако Ruby не имеет явного способа объявления интерфейсов).

В динамических языках Интерфейс к объекту часто подразумевается неявным образом, путем интроспекции объекта и запроса его, какие методы он предоставляет («Взгляд перед прыжком»), или, желательно, просто пытается использовать желаемый интерфейс для объекта и ловить исключения, если объект не обеспечивает этот интерфейс (проще просить прощения, чем разрешения). Это может привести к «ложным срабатываниям», когда два интерфейса имеют одно и то же имя метода, но семантически отличаются друг от друга, однако компромисс заключается в том, что ваш код более гибкий, поскольку вам не нужно указывать заранее, чтобы предвидеть все возможные применения вашего кода.


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


По умолчанию интерфейс обеспечивает слой для связи с другим кодом. Все общедоступные свойства и методы класса по умолчанию реализуют неявный интерфейс. Мы также можем определить интерфейс как роль, когда когда-либо какой-либо класс должен играть эту роль, он должен реализовать его, предоставляя ему различные формы реализации в зависимости от класса, реализующего его. Следовательно, когда вы говорите об интерфейсе, вы говорите о полиморфизме, и когда вы говорите о базовом классе, вы говорите о наследовании. Две концепции упс !!!


Унаследователь базового класса должен иметь отношение «есть». Интерфейс представляет собой «реализует» связь. Поэтому используйте только базовый класс, когда ваши наследники будут поддерживать отношения.


Что касается C #, то в некоторых смыслах интерфейсы и абстрактные классы могут быть взаимозаменяемыми. Однако различия заключаются в следующем: i) интерфейсы не могут реализовать код; ii) из-за этого интерфейсы не могут переходить к стеку до подкласса; и iii) только абстрактный класс может быть унаследован в классе, тогда как несколько классов могут быть реализованы в классе.







static-typing