Интерфейс или абстрактный класс?

Что касается моего нового Pet-Project, у меня есть вопрос по дизайну, который уже решен, но я хочу получить и другие мнения по этому поводу.

У меня два класса (упрощенные):

class MyObject
{
  string name {get;set;}
  enum relation {get;set;}
  int value {get;set;}
}

class MyObjectGroup
{
  string name {get;set;}
  enum relation {get;set;}
  int value {get;set;}
  List<MyObject> myobjects {get;set;}
}

Позже в Проекте MyObjectGroup и MyObject должны использоваться одинаково. Для этого я мог пойти двумя путями:

  • Создайте интерфейс: IObject
  • Создайте абстрактный класс: ObjectBase

Я решил пойти по пути интерфейса, так что позже в коде я не должен писать ObjectBase каждый раз, а IObject просто для удобства - но каковы еще плюсы этого способа?

А во-вторых, как насчет добавления IXmlSerializable ко всей истории? Позволить интерфейсу унаследовать от IXmlSerializable или у него есть больше плюсов для реализации IXmlSerializable в абстрактном базовом классе?


person Oliver Friedrich    schedule 22.07.2009    source источник
comment
Вот что нужно сделать: stackoverflow.com/questions/56867/interface-vs-base-class (Интерфейс против базового класса)   -  person Fredrik Mörk    schedule 22.07.2009
comment
возможный дубликат Почему и абстрактный класс, и интерфейс существует на C #?   -  person nawfal    schedule 07.07.2014


Ответы (15)


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

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

person cletus    schedule 22.07.2009
comment
С точки зрения потребителя абстрактный класс практически не имеет преимуществ перед интерфейсом. Абстрактный класс может быть более удобным для наследника, чем интерфейс, если он включает реализации для вещей, которые будут общими для многих или всех производных классов, и при условии, что у наследника нет неизбежного требования наследовать от какой-то другой класс. Наилучший подход - иметь абстрактный класс, который реализует интерфейс, и не использовать абстрактный класс для каких-либо целей, кроме как что-то унаследованное от. Вместо этого используйте переменные типа интерфейса. - person supercat; 29.11.2012

Это некоторые из различий между интерфейсами и абстрактными классами.

1A. Класс может наследовать (реализовывать) один или несколько интерфейсов. Итак, в C # интерфейсы используются для достижения множественного наследования.
1B. Класс может наследовать только один абстрактный класс.

2A. Интерфейс не может предоставлять какой-либо код, только подпись.
2B. Абстрактный класс может предоставлять полный код по умолчанию и / или только детали, которые необходимо переопределить.

3A. Интерфейс не может иметь модификаторы доступа к подпрограммам, функциям, свойствам и т. д., все считается общедоступным.
3B. Абстрактный класс может содержать модификаторы доступа для подпрограмм, функций и свойств.

4A. Интерфейсы используются для определения периферийных возможностей класса. Например, Ship и Car могут реализовывать IMovable интерфейс.
4B. Абстрактный класс определяет базовую идентичность класса и используется для объектов.

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

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

7A. В интерфейсе нельзя определять поля.
7B. В абстрактном классе могут быть определены поля и константы.

8A. Интерфейс не может иметь конструктора.
8B. В абстрактном классе могут быть реализованы конструкторы по умолчанию.

9A. Интерфейс может наследовать только от других интерфейсов.
9B. Абстрактный класс может наследовать от интерфейсов, абстрактного класса или даже класса.

person Venugopal M    schedule 07.05.2014
comment
Я отредактировал еще несколько в твою, надеюсь, все в порядке :) - person nawfal; 07.07.2014

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

Я бы не стал вовлекать IXmlSerializable, если бы мне не пришлось; это запутанный, хитрый интерфейс, который часто становится причиной неприятностей.

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

[XmlInclude(typeof(MyObject))] // : ObjectBase 
[XmlInclude(typeof(MyObjectGroup))] // : ObjectBase 
public abstract class ObjectBase { /*  */ }

(точный подход зависит от сериализатора)

person Marc Gravell    schedule 22.07.2009
comment
Я согласен. Вы можете выполнять сериализацию XML, даже не касаясь интерфейса, и вам это намного лучше. Если вам нужен пример кода для начала работы с сериализацией XML без IXmlSerializable, взгляните на этот вопрос, в нем есть некоторые (хотя и ограниченные) примеры того, как вызвать сериализатор: stackoverflow.com/questions/1115459/, а затем найдите Xml* атрибуты - person Matthew Scharley; 22.07.2009
comment
Это не работает, если у вас есть коллекции в качестве свойств, субэлементы не сериализуются автоматически при сериализации объекта. Хотя я не тестировал это на моно, я использую то, что использую сейчас. - person Oliver Friedrich; 22.07.2009
comment
Да, они ... просто (что раздражает) любой список (и т. Д.) Должен иметь общедоступный get / set и конкретный тип контейнера с конструктором без параметров, то есть public List<Customer> Customers {get;set;}. Вы можете обойти это с помощью DataContractSerializer, protobuf-net и т. Д. - ни для чего этого не требуется. - person Marc Gravell; 22.07.2009

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

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

person mmx    schedule 22.07.2009

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

Базовый класс предлагает возможность построить «дерево» наследования, в котором более сложные классы (общего «типа») строятся на основе более простых «базовых» классов. Классическим и раздражающим примером в объектно-ориентированном стиле обычно является базовый класс Shape, который наследуется от Triangle, Square и т. Д.

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

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

person Lazarus    schedule 22.07.2009

При проектировании классов архитектор думает о двух вещах.

  1. Поведение объекта.
  2. реализация объекта.

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

Давайте рассмотрим сценарий разработки, в котором вещи (запрос, модель класса и т. Д.) Меняются очень часто, и вам необходимо предоставить определенные версии приложения.

Первоначальная постановка проблемы: вам необходимо создать класс «Поезд» для индийской железной дороги, который в 1970 году имел поведение maxSpeed.

1. Бизнес-моделирование с абстрактным классом

V 0.0 (Начальная проблема) Первоначальная постановка задачи: вам необходимо создать класс Train для индийской железной дороги, который в 1970 году имел поведение maxSpeed.

public abstract class Train {
    public int maxSpeed();
}

V 1.0 (измененная проблема 1) изменила постановку задачи: вам необходимо создать Diesel Train класс для индийской железной дороги, который имеет поведение maxSpeed, в 1975 году.

public abstract class DieselTrain extends train {
     public int maxFuelCapacity ();
}

V 2.0 (измененная проблема 2) изменила формулировку проблемы: вам необходимо создать ElectricalTrain класс для индийской железной дороги, который в 1980 году имел поведение maxSpeed, maxVoltage.

public abstract class ElectricalTrain extends train {
     public int maxvoltage ();
}

Версия 3.0 (измененная проблема 3)

измененная формулировка проблемы: вам нужно создать класс HybridTrain (использует как дизельное топливо, так и электричество) для индийской железной дороги, который имеет поведение maxSpeed, maxVoltage, maxVoltage в 1985 году.

public abstract class HybridTrain extends ElectricalTrain , DisealTrain {
    { Not possible in java }
}
{here Business modeling with abstract class fails}

2. Бизнес-моделирование с интерфейсом

Просто измените слово abstract на interface и… ваше бизнес-моделирование с интерфейсом будет успешным.

http://javaqna.wordpress.com/2008/08/24/why-the-use-on-interfaces-instead-of-abstract-classes-is-encouraged-in-java-programming/

person Community    schedule 23.02.2011

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

Например, если вы реализуете иерархию классов для транспортных средств, реализуйте интерфейс под названием Vehicle, который имеет такие свойства, как Color MaxSpeed ​​и т. д., и такие методы, как Drive (). Все дочерние классы, такие как Car Scooter AirPlane SolarCar и т. Д., Должны быть производными от этого базового интерфейса, но обеспечивать отдельную реализацию методов и свойств, предоставляемых Vehicle.

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

Например, если вы реализуете класс под названием SpaceShip, который должен иметь функциональные возможности от Транспортного средства, а также от НЛО, тогда сделайте и Транспортное средство, и НЛО в качестве интерфейсов, а затем создайте класс SpaceShip, который реализует как Транспортное средство, так и НЛО.

Абстрактные классы:

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

Например, снова возьмем пример класса Vehicle выше. Если мы хотим, чтобы все классы, производные от Vehicle, реализовывали метод Drive () фиксированным образом, тогда как другие методы могут быть переопределены дочерними классами. В таком сценарии мы реализуем класс Vehicle как абстрактный класс с реализацией Drive, а другие методы / свойства оставляем абстрактными, чтобы их могли переопределить дочерние классы.

-> Цель абстрактного класса - предоставить общее определение базового класса, которым могут совместно пользоваться несколько производных классов.

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

person user3824027    schedule 19.06.2016

Вы могли бы пойти с ОБОИМИ. ObjectBase избавляет вас от необходимости реализовывать общие свойства более одного раза и реализует IObject за вас. Везде, где вы его используете, обратитесь к IObject, чтобы вы могли позже провести тестирование с помощью макетов.

person n8wrl    schedule 22.07.2009

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

person Marcin Deptuła    schedule 22.07.2009

При прочих равных, идите с интерфейсом. Легче имитировать для модульного тестирования.

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

person Sterno    schedule 22.07.2009

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

person SaguiItay    schedule 22.07.2009

Если у вас есть функция в классе, вы должны использовать класс abstact вместо интерфейса. Как правило, интерфейс используется от имени типа.

person Edwin Tai    schedule 22.07.2009

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

person David Rutten    schedule 22.07.2009

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

Абстрактные классы позволяют вам определять некоторые поведения; они заставляют ваши подклассы предоставлять других. Например, если у вас есть структура приложения, абстрактный класс может предоставлять услуги по умолчанию, такие как обработка событий и сообщений. Эти службы позволяют вашему приложению подключаться к структуре вашего приложения. Однако есть некоторые специфические для приложения функции, которые может выполнять только ваше приложение. Такие функции могут включать задачи запуска и завершения работы, которые часто зависят от приложения. Поэтому вместо того, чтобы пытаться определить это поведение как таковое, абстрактный базовый класс может объявлять абстрактные методы выключения и запуска. Базовый класс знает, что ему нужны эти методы, но абстрактный класс позволяет вашему классу признать, что он не знает, как выполнять эти действия; он знает только то, что должен инициировать действия. Когда пришло время начать работу, абстрактный класс может вызвать метод запуска. Когда базовый класс вызывает этот метод, Java вызывает метод, определенный дочерним классом.

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

person Chaitanya    schedule 20.12.2013

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

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

person Evgeniy    schedule 30.03.2016