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

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

Когда дело доходит до реализации, как мне решить перейти к базовому типу или интерфейсу? Я попытался выработать несколько примеров, но я не получу полную идею: (

Примеры того, как и почему были бы очень благодарны.

4b9b3361

Ответ 1

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

Пример:

class Person
{
    string Name { get; set; }
}

class Employee : Person
{
    string Company { get; set; }
}

Для Employee имеет смысл наследовать от Person, потому что класс Employee не должен определять свойство Name, поскольку он разделяет реализацию.

interface IPolygon
{
    double CalculateArea()
}

class Rectangle : IPolygon
{
    double Width { get; set; }
    double Height { get; set; }

    double CalculateArea()
    {
        return this.Width * this.Height;
    }
}

class Triangle : IPolygon
{
    double Base { get; set; }
    double Height { get; set; }

    double CalculateArea()
    {
        return 0.5 * this.Base * this.Height;
    }
}

Поскольку Rectangle и Triangle имеют такие разные реализации CalculateArea, им не имеет смысла наследовать их из базового класса.

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

И, как указано в j__m, вы не можете наследовать от нескольких базовых классов, но вы можете реализовать несколько интерфейсов.

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

Ответ 2

Чтобы решить, использовать ли абстрактный класс или интерфейс, я нахожу эту статью очень полезной Source:

Хороший способ отличить один случай от другого для меня всегда был следующим:

  1. Много ли классов можно "сгруппировать" и описать одним существительным? Если это так, имейте абстрактный класс по имени этого существительного и наследуйте классы от него. (Решающим фактором является то, что эти классы совместно используют функциональность, и вы никогда не будете создавать экземпляр только Animal... вы всегда будете создавать экземпляр определенного типа Animal: реализация вашего Животный базовый класс) Пример: Cat и Dog могут оба наследовать от абстрактного класса Animal, и этот абстрактный базовый класс будет реализовывать метод void Breathe() таким образом, все животные будут делать то же самое. (Я мог бы сделать этот метод виртуальным, чтобы переопределить его для определенных животных, таких как Рыба, которая не дышит так же, как большинство животных).

  2. Какие виды глаголов могут применяться к моему классу, которые в целом могут также применяться к другим? Создайте интерфейс для каждого из этих глаголов. Пример: всех животных можно кормить, поэтому я создам интерфейс IFeedable и сделаю так, чтобы Animal реализовал это. Только Dog и Horse достаточно хороши для реализации ILikeable - я не буду реализовывать это в базовом классе, поскольку это не относится к Cat.

Пожалуйста, посмотрите также на этот вопрос Интерфейс против Базового класса.

Ответ 3

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

Интерфейс не позволяет вам определять контракты конструктора.

В приведенном ниже примере каждый объект Animal должен иметь ИМЯ. Это невозможно выполнить с помощью интерфейса.

public abstract class Animal
{
    public Animal(string name)
    {
        this.Name = name;
    }

    public string Name 
    { 
        get; 
        private set; 
    }
}

public class Cat : Animal
{
    public Cat(string name)
        : base(name)
    {

    }

    string NoOfLegs { get; set; }
}



class Program
{
    static void Main(string[] args)
    {
        Animal aCat = new Cat("a");
    }
}

Ответ 4

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

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

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

Ответ 5

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

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

Ответ 6

Я нашел здесь аналогию:

Абстрактный базовый класс: - Производитель автомобилей может разработать бензиновый двигатель, который имеет несколько вариантов (1,6, 2 л и т.д.). Литье блоков двигателя можно рассматривать как абстрактный базовый класс, т.е. Определяет основную форму и особенности движка. Версия 2L может иметь большую головку цилиндра и т.д.

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

Ответ 7

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

С интерфейсами вы более гибки. Прежде всего вы делаете объявление между классами, которые унаследованы от этого интерфейса. С этим

  • Вы делаете свой код более читабельным
  • Вы разделяете свои концепции, как класс, унаследованный от интерфейса A, может подключаться к базе данных Mssql, а другой - к базе данных Mysql.
  • Ваш код становится более понятным; Interfaces helps to reduce coupling and therefore allow you to easily interchange implementations for the same concept without the underlying code being affected
  • Вы можете использовать внедрение зависимости
  • Вы можете создать юнит-тест проще.

    Вы можете посмотреть этот вопрос здесь для получения дополнительной информации.