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

Причина использования BOTH абстрактных классов и интерфейсов? (Абстрактный класс реализует интерфейс)

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

  // This describes a person....
  public interface IPerson
  {
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int BasePay { get; set; }
    public string Address { get; set; }
  }

  // And so does this, but it also uses the interface....
  public abstract class Person : IPerson
  {
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int BasePay { get; set; }
    public string Address { get; set; }
  }

  // This uses both ?!
  public class CoalMiner : Person, IPerson
  {
    public CoalMiner()
    {
      BasePay = 10000;
    }
  }

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

4b9b3361

Ответ 1

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

Интерфейсы должны использоваться для контрактов - все люди всегда Person, поэтому абстрактный класс здесь имеет больше смысла. Интерфейсы могут быть добавлены для поведения, привязанного к определенным типам людей, или для того, чтобы позволить Person использоваться определенным образом, выполнив контракт (например: Person : IComparable<Person>).

Ответ 2

Наличие интерфейса IPerson и базового класса Person позволяет вам определенные свободы, если вы обходите объекты в интерфейсе IPerson, а не в базовом классе Person.

Базовые классы имеют тенденцию реализовывать общий код, который должен использоваться всеми потомками этого базового класса. Это прекрасно, если вы этого хотите, но можно столкнуться с ситуацией, когда требуется совсем другая реализация IPerson, где базовый класс Person вообще не используется. Теперь у вас есть две иерархии классов, которые имеют IPerson, и все еще работает. Вы не сможете сделать это только с помощью Person.

Еще одна веская причина для избыточности всегда наличия интерфейса - для COM-взаимодействия.

Ответ 3

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

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

Явное упоминание интерфейса иногда делает его более читаемым. Документация MSDN часто делает это, например. показывая, что List<> реализует как ICollection<>, так и IList<>, хотя IList<> получен из ICollection<>.

Ответ 4

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

Ответ 5

Интерфейсы определяют контракт на поведение, поэтому это имеет смысл только в том случае, если у вас есть достаточное поведение (помимо простых аксессуаров свойств), которое не-Person может захотеть реализовать IPerson. Например, если IPerson мог HoldConversation(Stream input, Stream output), тогда у вас может быть TuringTestCandidate, который реализует IPerson, фактически не получая от Person.

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

Ответ 6

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

Если вы должны были применять методы на объектах с двумя руками и двумя ногами → IThingWithArmsAndLegs::DoTheHokeyPokey(), это могло бы быть хорошим использованием интерфейса. Тогда этот интерфейс может быть разделен между Person : IThingWithArmsAndLegs и Alien : IThingWithArmsAndLegs.

Ответ 7

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

Сказав это, я бы не рекомендовал эту практику вообще.

Ответ 8

Мне кажется, что он плохо выглядит Person и IPerson в объявлении CoalMiner. Я бы просто получил его от Person. Интерфейс структурирования → абстрактный класс → конкретный класс в порядке со мной, но в большинстве ситуаций излишний. Я иногда использую его, если большинство классов, реализующих интерфейс, имеют много кода. Таким образом, исходя из абстрактного класса, это случай по умолчанию для 95% простых случаев, но я бы хотел, чтобы опция имела полностью независимую реализацию интерфейса.

Ответ 9

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

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

Ответ 10

Пока нет NEED для CoalMiner для интерфейса IPerson, я знаю, что некоторые люди предпочитают это, поэтому очевидно, что тип реализует интерфейс. Это, как говорится, я не так очень полезен.

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

public interface ICustomer {}

public abstract class SapEntity {}
public abstract class NHibernateEntity {}    

public class SapCustomer : SapEntity, ICustomer {}
public class NHibernateCustomer : NHibernateEntity, ICustomer {}

public class CustomerProcessor
{
   public ICustomer GetCustomer(int customerID)
   {
      // business logic here
   }
}

Ответ 11

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

Ответ 12

Я поклонник обоих в правильной ситуации, почему?

Даже если вам нужен только интерфейс для некоторого типа IOC/DI, он не предоставляет общих функций. Вы можете сделать это с помощью Inject и иметь базовую функциональность, охватываемую общим абстрактным базовым классом. Тогда только абстрактные/виртуальные методы по мере необходимости.

Это ооп у него лучший ИМХО. Особенно в многоцелевом решении.

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

Ответ 13

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

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

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

Ответ 14

Нет, если вы дважды расширяете один и тот же интерфейс, он используется только в первый раз. Вы можете удалить второй IPerson, и ваш код все равно будет работать нормально.