Какие принципы использовать для отбора классов для наследования
Что проверяют
Понимаете ли вы, что наследование это самая сильная связь между классами, и выбираете ли его осознанно. Следом обычно дают пример вроде Square extends Rectangle или Stack extends Vector и просят объяснить, что с ним не так.
Как отвечать
Сформулируйте критерии, по которым наследование оправдано:
- настоящее отношение «является»: подкласс это частный случай родителя во всех контекстах, а не только по названию. Если общего лишь несколько полей, это повод для композиции;
- принцип подстановки Лисков: наследник выполняет контракт родителя, не усиливает предусловия и не ослабляет постусловия.
Square extends Rectangleломается наsetWidth: код, который ждёт прямоугольник, получает неожиданное поведение; - стабильный базовый класс: родитель, который часто меняется, тянет за собой всех наследников;
- общее поведение, а не общий код: ради переиспользования пары методов лучше выделить отдельный класс и держать его полем.
Дальше назовите инструменты Java. Интерфейсы с default-методами дают общее поведение без жёсткой иерархии. Абстрактный класс уместен, когда есть общее состояние и шаблонный метод. final запрещает наследование там, где класс для него не проектировался, а sealed с Java 17 разрешает только перечисленных наследников, и компилятор это проверяет.
Хорошо сослаться на совет из Effective Java: предпочитайте композицию наследованию, а класс либо проектируйте и документируйте для наследования, либо запрещайте его. Пример хрупкого базового класса: наследник HashSet, который считает добавленные элементы, переопределяет add и addAll и получает двойной счёт, потому что унаследованный addAll внутри вызывает add.
Где спотыкаются
Выбирают родителя по общим полям. Employee extends Person выглядит логично, пока сотрудник не становится ещё и клиентом, и иерархия перестаёт описывать реальность.
Путают LSP с тем, что наследник компилируется на месте родителя. Компилятор проверяет типы, а не поведение, и нарушение видно только по контракту.
Строят глубокие иерархии ради переиспользования. На четвёртом уровне уже непонятно, откуда пришёл метод, а правка в корне ломает то, что никто не проверял.