В языке Java абстрактный класс и интерфейс — это два механизма, которые поддерживают определение абстрактного класса. Именно благодаря существованию этих двух механизмов Java обладает мощными объектно-ориентированными возможностями. Между абстрактным классом и интерфейсом существует большое сходство с точки зрения поддержки определения абстрактного класса, и их даже можно заменить друг другом. Поэтому многие разработчики, похоже, более небрежно относятся к выбору абстрактного класса и интерфейса при определении абстрактных классов. На самом деле между ними все еще существует большая разница. Их выбор даже отражает понимание природы проблемной области и правильность и разумность понимания замысла проекта. В этой статье мы проанализируем различия между ними и попытаемся предоставить разработчикам основу для выбора между ними.
Понимание абстрактных классов
Абстрактный класс и интерфейс используются для определения абстрактных классов на языке Java (абстрактный класс в этой статье не переводится с абстрактного класса, он представляет собой абстрактное тело, а абстрактный класс используется для определения абстрактных классов на языке Java) метод, пожалуйста, обратите внимание на различие), так что же такое абстрактный класс и какие преимущества может принести нам использование абстрактных классов?
В объектно-ориентированной концепции мы знаем, что все объекты представлены классами, но обратное неверно. Не все классы используются для описания объектов. Если класс не содержит достаточно информации для описания конкретного объекта, такой класс является абстрактным. Абстрактные классы часто используются для представления абстрактных концепций, которые мы получаем в результате анализа и проектирования проблемных областей. Они представляют собой абстракции ряда конкретных концепций, которые выглядят по-разному, но по сути одинаковы. Например: если мы разработаем программное обеспечение для редактирования графики, мы обнаружим, что в проблемной области есть некоторые конкретные понятия, такие как круги и треугольники. Они разные, но все они относятся к понятию формы. Понятие формы различно. в проблемной области, если оно существует, то это абстрактное понятие. Именно потому, что абстрактные концепции не имеют соответствующих конкретных концепций в проблемной области, абстрактные классы, используемые для представления абстрактных концепций, не могут быть созданы.
В объектно-ориентированной области абстрактные классы в основном используются для сокрытия типов. Мы можем построить абстрактное описание фиксированного набора поведений, но этот набор поведений может иметь любое количество возможных конкретных реализаций. Это абстрактное описание является абстрактным классом, и этот набор любых возможных конкретных реализаций представлен всеми возможными производными классами. Модули могут работать с абстрактным телом. Поскольку модуль опирается на фиксированную абстракцию, его нельзя одновременно изменить, поведение этого модуля также можно расширить, производя его от этой абстракции; Читатели, знакомые с OCP, должны знать, что для реализации OCP (принципа открытости-закрытости), одного из основных принципов объектно-ориентированного проектирования, ключевым моментом являются абстрактные классы.
Взгляд на абстрактный класс и интерфейс с уровня грамматического определения.
На грамматическом уровне язык Java предоставляет различные методы определения абстрактного класса и интерфейса. Ниже приведен пример определения абстрактного класса с именем Demo, иллюстрирующий эту разницу.
Способ определения абстрактного класса Demo с использованием абстрактного класса следующий:
абстрактный класс Demo{
абстрактный метод void1();
абстрактный метод void2();
…
}
Способ определения абстрактного класса Demo с использованием интерфейса следующий:
Демо-интерфейс {
недействительный метод1();
недействительный метод2();
…
}
В методе абстрактного класса Demo может иметь свои собственные элементы данных или неабстрактные методы-члены. В методе интерфейса Demo могут иметь только статические элементы данных, которые не могут быть изменены (то есть они должны быть статическими окончательными, но data). члены обычно не определяются в интерфейсе), а все методы-члены являются абстрактными. В некотором смысле интерфейс — это особая форма абстрактного класса.
С точки зрения программирования как абстрактный класс, так и интерфейс могут использоваться для реализации идеи «проектирования по контракту». Однако все же существуют некоторые различия в конкретном использовании.
Прежде всего, абстрактный класс представляет собой отношение наследования в языке Java, и класс может использовать отношение наследования только один раз (поскольку Java не поддерживает множественное наследование - примечание к переносу). Однако класс может реализовывать несколько интерфейсов. Возможно, это компромиссное решение разработчиков языка Java при рассмотрении поддержки Java множественного наследования.
Во-вторых, в определении абстрактного класса мы можем назначить поведение метода по умолчанию. Но в определении интерфейса методы не могут иметь поведение по умолчанию. Чтобы обойти это ограничение, необходимо использовать делегаты, но это добавит некоторую сложность и иногда вызовет массу проблем.
Существует еще одна серьезная проблема, связанная с невозможностью определить поведение по умолчанию в абстрактном классе: это может вызвать проблемы с обслуживанием. Потому что, если вы позже захотите изменить интерфейс класса (обычно представленный абстрактным классом или интерфейсом) для адаптации к новым ситуациям (например, добавив новые методы или добавив новые параметры к уже используемым методам), это будет очень хлопотно. может занять много времени (особенно если имеется много производных классов). Но если интерфейс реализован через абстрактный класс, вам может потребоваться только изменить поведение по умолчанию, определенное в абстрактном классе.
Аналогично, если поведение по умолчанию не может быть определено в абстрактном классе, одна и та же реализация метода появится в каждом производном классе абстрактного класса, что нарушает принцип «одно правило, одно место», что приводит к дублированию кода, что также вредно для будущее обслуживание. Поэтому будьте очень осторожны при выборе между абстрактным классом и интерфейсом.
Взгляд на абстрактный класс и интерфейс с уровня концепции дизайна
Вышеупомянутое в основном обсуждает разницу между абстрактным классом и интерфейсом с точки зрения грамматического определения и программирования. Различия на этих уровнях относительно низкоуровневые и несущественные. В этом разделе будет проанализирована разница между абстрактным классом и интерфейсом с другого уровня: концепции дизайна, отраженные в них. Автор считает, что только анализируя с этого уровня, мы сможем понять суть двух понятий.
Как упоминалось ранее, абстрактный класс воплощает отношения наследования в языке Java. Чтобы сделать отношения наследования разумными, между родительским классом и производным классом, то есть между родительским классом и производным классом, должно существовать отношение «есть-а». производный класс имеет ту же концепцию. По сути, он должен быть таким же. Это не относится к интерфейсам. Разработчик интерфейса и определение интерфейса не обязаны быть концептуально согласованными, а лишь реализовывать контракт, определенный интерфейсом. Чтобы облегчить понимание дискуссии, ниже будет проиллюстрирован простой пример.
Рассмотрим такой пример. Предположим, что в нашей проблемной области существует абстрактное понятие Дверь. Дверь имеет два действия: открытие и закрытие. На данный момент мы можем определить тип, который представляет абстрактное понятие через абстрактный класс или интерфейс. методы следующие:
Используйте абстрактный класс для определения двери:
абстрактный класс Door{
абстрактная пустота open();
абстрактная пустота close();
}
Определите дверь, используя метод интерфейса:
интерфейс Дверь{
недействительный открытый();
недействительно закрыть();
}
Другие конкретные типы дверей могут расширять дверь, определенную с помощью метода абстрактного класса, или реализовывать дверь, определенную с помощью метода интерфейса. Кажется, нет большой разницы между использованием абстрактного класса и интерфейса.
Если дверь теперь должна иметь функцию сигнализации. Как нам следует спроектировать структуру класса для этого примера (в этом примере главным образом показано различие в концепциях проектирования между абстрактным классом и интерфейсом, а другие не относящиеся к делу вопросы упрощены или проигнорированы)? Возможные решения перечислены ниже, и эти различные варианты анализируются на уровне концепции проекта.
Решение первое:
Просто добавьте метод сигнализации в определение двери, как показано ниже:
абстрактный класс Door{
абстрактная пустота open();
абстрактная пустота close();
абстрактная недействительная сигнализация();
}
или
интерфейс Дверь{
недействительный открытый();
недействительно закрыть();
аннулировать сигнал тревоги();
}
Тогда AlarmDoor с функцией сигнализации определяется следующим образом:
класс AlarmDoor расширяет Door{
недействительный открытый(){…}
недействительно закрыть(){…}
аннулировать сигнал тревоги(){…}
}
или
класс AlarmDoor реализует Door{
недействительный открытый(){…}
недействительно закрыть(){…}
аннулировать сигнал тревоги(){…}
}
Этот метод нарушает ISP (принцип разделения интерфейса), основной принцип объектно-ориентированного проектирования. В определении двери метод поведения, присущий самой концепции двери, смешивается с методом поведения другой концепции «сигнализация». Одна из проблем, вызванных этим, заключается в том, что модули, которые полагаются только на концепцию двери, будут меняться из-за изменений в концепции «тревоги» (например, изменение параметров метода тревоги) и наоборот.
Решение второе:
Поскольку открытие, закрытие и сигнализация относятся к двум разным понятиям, согласно принципу ISP, они должны быть определены в абстрактных классах, представляющих эти два понятия. Методы определения: обе концепции определяются с использованием метода абстрактного класса; обе концепции определяются с использованием метода интерфейса; одна концепция определяется с использованием метода абстрактного класса, а другая концепция определяется с использованием метода интерфейса.
Очевидно, что поскольку язык Java не поддерживает множественное наследование, невозможно определить обе концепции с помощью абстрактного класса. Оба последних метода осуществимы, но их выбор отражает понимание сути концепции в проблемной области и правильность и разумность отражения замысла проекта. Давайте проанализируем и объясним одно за другим.
Если обе концепции определены с использованием метода интерфейса, то это отражает две проблемы: 1. Мы можем не совсем четко понимать предметную область. Является ли AlarmDoor по сути дверью или сигнализацией? 2. Если с нашим пониманием проблемной области нет проблем, например: путем анализа проблемной области мы обнаруживаем, что AlarmDoor концептуально соответствует Door, то мы не сможем правильно раскрыть замысел нашего проекта при его реализации. , т.к. определения этих двух понятий (оба определены с помощью метода интерфейса) не отражают приведенный выше смысл.
Если наше понимание проблемной области таково: AlarmDoor концептуально по сути является дверью, и она также имеет функцию сигнализации. Как нам следует спроектировать и реализовать его, чтобы четко отразить наше значение? Как упоминалось ранее, абстрактный класс представляет собой отношение наследования в языке Java, и отношение наследования по сути представляет собой отношение «есть-а». Итак, для определения концепции двери нам следует использовать метод абстрактного класса. Кроме того, AlarmDoor имеет функцию сигнализации, что означает, что она может выполнять поведение, определенное в концепции сигнализации, поэтому концепцию сигнализации можно определить через интерфейс. Как показано ниже:
абстрактный класс Door{
абстрактная пустота open();
абстрактная пустота close();
}
интерфейсAlarm {
аннулировать сигнал тревоги();
}
класс Alarm Door расширяет Door реализует Alarm{
недействительный открытый(){…}
недействительно закрыть(){…}
аннулировать сигнал тревоги(){…}
}
Этот метод реализации может в основном четко отражать наше понимание проблемной области и правильно раскрывать наши проектные замыслы. Фактически, абстрактный класс представляет собой отношение «есть-а», а интерфейс представляет собой отношение «как-а». Вы можете использовать его в качестве основы при выборе. Конечно, это основано на понимании предметной области. пример: Если мы. Если AlarmDoor по своей сути является сигнализацией и выполняет функцию двери, то приведенное выше определение необходимо изменить на противоположное.
краткое содержание
1.Абстрактный класс представляет отношение наследования в языке Java, и класс может использовать отношение наследования только один раз. Однако класс может реализовывать несколько интерфейсов.
2. В абстрактном классе вы можете иметь свои собственные элементы данных, а также вы можете иметь неабстрактные методы-члены, но в интерфейсе вы можете иметь только статические элементы данных, которые не могут быть изменены (то есть они должны быть статическими окончательными, но в интерфейсе элементы данных обычно не определяются), и все методы-члены являются абстрактными.
3.Абстрактный класс и интерфейс отражают различные концепции дизайна. Фактически, абстрактный класс представляет отношение «есть-а», а интерфейс представляет отношение «как-а».
4. Классы, реализующие абстрактные классы и интерфейсы, должны реализовывать в себе все методы. Абстрактные классы могут иметь неабстрактные методы. В интерфейсе не может быть методов реализации.
5. Переменные, определенные в интерфейсе, по умолчанию являются общедоступными статическим окончательными, и их начальное значение должно быть задано, поэтому их нельзя переопределить в классе реализации, а также нельзя изменить их значения.
6. Переменные в абстрактных классах по умолчанию дружественны, а их значения можно переопределять в подклассах или переназначать.
7. По умолчанию методы интерфейса имеют открытый и абстрактный тип.
в заключение
Абстрактный класс и интерфейс — это два способа определения абстрактных классов в языке Java, и они очень похожи. Однако их выбор часто отражает понимание сущности понятия в проблемной области и правильность и разумность отражения замысла проектирования, поскольку они выражают разные отношения между понятиями (хотя все они могут достигать требуемых функций). На самом деле это своего рода идиоматическое использование языка. Я надеюсь, что читатели смогут его внимательно понять.