В большинстве случаев, когда дискуссия возникает между принятием решения о том, следует ли использовать интерфейсы или абстрактные классы, она заканчивается определениями того, как их использовать, но не всегда, почему и когда? Также не всегда возникают очевидные другие конкретные классы и служебные классы, которые вы можете использовать в конечном итоге. На самом деле, по моему мнению, правильный способ ответить на вопрос - определить контекст, с которым вы имеете дело в отношении объектов домена или сущности, а именно, каков ваш вариант использования?
На очень высоком уровне Java состоит из объектов (объектов или доменных объектов, которые могут моделировать объекты в реальном мире), которые взаимодействуют друг с другом с помощью методов. В любом случае вы хотите смоделировать поведение с интерфейсами и использовать абстрактные классы, когда у вас есть наследование.
По моему личному опыту, я делаю это, используя подход сверху вниз, а затем снизу вверх. Я начинаю искать наследование, рассматривая сценарий использования и выясняя, какие классы мне понадобятся. Затем я смотрю, есть ли superClassOrInterfaceType (поскольку классы и интерфейсы определяют типы, я объединяю их в одно слово для простоты. Надеюсь, это не делает его более запутанным) объект домена, который будет охватывать все объекты, как в superClassOrInterfaceType транспортного средства, если я работаю над сценарием использования, имеющим дело с subtypeClassOrInterfaceTypes, например: автомобили, грузовики, джипы и мотоциклы, например. Если есть отношение иерархии, то я определяю superClassOrInterfaceType и subtypeClassOrInterfaceTypes.
Как я уже сказал, в первую очередь я ищу общий домен superClassOrInterfaceType для объектов, с которыми я имею дело. Если это так, я ищу общие операции метода между subtypeClassOrInterfaceTypes. Если нет, я посмотрю, есть ли общие реализации методов, потому что, хотя у вас может быть superClassOrInterfaceType и могут быть общие методы, реализации могут не поддерживать повторное использование кода. На данный момент, если у меня есть общие методы, но нет общих реализаций, я склоняюсь к интерфейсу. Однако в этом упрощенном примере у меня должны быть общие методы с некоторыми общими реализациями для транспортного средства subtypeClassOrInterfaceTypes, с которыми я могу повторно использовать код.
С другой стороны, если нет структуры наследования, я начинаю снизу вверх, чтобы посмотреть, есть ли общие методы. Если нет общих методов и общих реализаций, я выбираю конкретный класс.
Как правило, если существует наследование с общими методами и общими реализациями и потребность в нескольких методах реализации подтипа в одном и том же подтипе, то я использую абстрактный класс, что редко, но я его использую. Если вы просто используете абстрактные классы только из-за наследования, вы можете столкнуться с проблемами, если код сильно изменится. Это очень подробно описано в приведенном здесь примере: Интерфейсы против абстрактных классов в Java , для различных типов доменных объектов двигателей. Для одного из них требовался двигатель с двойным питанием, который требовал использования нескольких методов реализации подтипа в одном классе подтипа.
Подводя итог, как правило, вы хотите определить поведение (что будут делать объекты) с интерфейсами, а не с абстрактными классами. Абстрактные классы ориентированы на иерархию реализации и повторное использование кода.
Вот несколько ссылок, которые более подробно рассказывают об этом.
Спасибо Тип и Нежный класс
Магия подтипа Полиморфизм
Максимальная гибкость с интерфейсами и абстрактными классами
Интерфейсы против абстрактных классов в Java