Абстрактные классы и интерфейсы — когда применять одно или другое?

Здравствуйте
Насколько я понял, абстрактные классы используются для выделения общности в реализации, а интерфейсы - для общности в поведении. Но почему общность поведения нельзя выделять в абстрактных методах?
К примеру, есть абстрактный класс животное с методом дышать. Наследники реализуют интерфейс с методом "издать звук". Но почему этот метод нельзя взять как абстрактный в классе "животное"? Когда нужно применять одно или другое?
  • Вопрос задан
  • 697 просмотров
Пригласить эксперта
Ответы на вопрос 3
@yociyavi
Как по мне так эти ассоциации из реального мира только усложняют понимание ООП.
Интерфейс - это описание методов доступа к объекту.
Абстрактный класс - это выделение общих методов и свойств классов.
Ответ написан
Комментировать
@Mercury13
Программист на «си с крестами» и не только
Всё зависит от того, что вы хотите. И вообще, с «опциональной» функциональностью есть много вариантов (для простоты пишу на Java).

1. «Типа по ООП».
class Animal {}
class Dog extends Animal {
   public void doSound() {}
}

Недостаток: если у нас есть собака со звуком, кошка со звуком и утка со звуком и надо выдать звук, если возможно — фигвам!

2. Реализовать интерфейс Vocal
interface Vocal {
  void doSound();
}
class Animal {}
class Dog extends Animal implements Vocal {
   @Override
   public void doSound();
}

В таком случае
if (animal instanceof Vocal)
  ((Vocal)animal).doSound().

Впрочем, такие преобразования типа — тоже слегка не по ООП.

3. Не в курсе, возможно ли в Java, напишу это на C++. Protected doSound в Animal и public в Dog.
class Animal {
protected:
   void doSound();
};
class Dog : public Animal {
public:
  using Animal::doSound;
};

Недостаток в том, что если всё же придётся организовывать общую функциональность — то приходится писать шаблон «Public Морозов».
Пример: у всех компонентов VCL есть protected __property Caption. И в 99% случаев этого хватает: заглавие отображается где-то — вытягивай наружу. У меня возник вопрос с автоматическим переводом форм. Либо подключай интроспекцию, либо Public Морозов (в Delphi/Builder есть интроспекция и доп. право доступа published, подключающее свойство к ней). Я не стал мучиться и сделал второе.
Также задача несколько неудобна, когда библиотека долго живёт и развивается: с каждой новой версией приходится выносить наружу всё новые и новые свойства
Плюсы? Просто, малый расход памяти и удобно писать специальные задачи. Например, свойство Hint protected, но действует; если всплывающая подсказка какая-нибудь динамическая и снаружи менять нельзя — меняй на здоровье изнутри.

ЗЫ. Пришёл с работы, то же самое на Java.
public class Dog extends Animal {
    @Override
    public void doSound() { super.doSound(); }
}

Ну а роль Морозова будут играть API интроспекции и «морозовский» класс в том же пакете. Все мы забываем, что protected покрывает более жёсткое package, т.е. из того же пакета тоже можно.
public static void main(String[] args) {
        Animal an = new Animal();
        an.doSound();   // protected!
    }


4. Может ли издавать звук?
class Animal {
public boolean isVocal() { return false; }
public void doSound() {}
}


5. Вернуть интерфейс Vocal; если null — животное молчит.
class Animal {
public Vocal getVocal() { return null; }
}


Что выбирать — однозначного ответа нет. Насколько много будут наследовать от этого класса, насколько много будет общих задач и насколько будет перегруженной документация… Допустим, если мы не имеем доступа к классу Animal, заманчиво второе. А если Vocal — не интерфейс, а абстрактный класс, то пятое.
Ответ написан
Комментировать
@shaqster
Symfony3 Guru
Интерфейс - когда нужно единообразно работать с разными объектами, имеющими одинаковое логическое назначение (репозитории сущностей, например).
Абстрактный класс - когда нужно определить некое единообразное поведение для всех производных объектов,а также различия в поведении - абстрактные методы.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы