Реализации абстрактных методов по умолчанию

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

Если у меня есть класс Parent-A с абстрактным методом. Будет только 2 ребенка. Если Child-B реализует AbstractMethodA, а Child-B нет, поскольку он не применяется.

Нужно ли мне

  1. Удалить ключевое слово abstract из родительского и использовать виртуальный или динамический?
  2. Предоставьте пустую реализацию метода.
  3. Предоставьте реализацию, которая вызывает ошибку при вызове.
  4. Игнорируйте предупреждение.

Обновлено: спасибо за все ответы. Это подтвердило мои подозрения, что этого не должно происходить. После дальнейшего расследования выяснилось, что эти методы вообще не использовались, поэтому я полностью их удалил.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
602
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

сделать его виртуально пустым в базовом классе и переопределить в дочерних.

Ответ принят как подходящий

Если AbstractMethodA не применяется к Child-B, то Child-B не должен наследовать от Parent-A.

Или взять контрапозитив, если Child-B наследуется от Parent-A, а AbstractMethodA не применяется к дочернему, то его тоже не должно быть в родительском.

Помещая метод в Parent-A, вы говорите, что метод применяется к Parent-A и всем его дочерним элементам. Вот что такое наследование средства, и если вы используете его для обозначения чего-то другого, вы столкнетесь с серьезным спором со своим компилятором.

[Изменить - тем не менее, ответ Младена Прайдича прекрасен, если метод действительно применяется, но не должен ничего делать для одного или нескольких задействованных классов. Метод, который ничего не делает, - это ИМО, это не то же самое, что метод, который неприменим, но, возможно, мы не имеем в виду то же самое, говоря «не применяется»]

Другой способ - все равно реализовать метод в Child-B, но заставить его делать что-то радикальное, например, всегда возвращать ошибку, или генерировать исключение, или что-то в этом роде. Это работает, но его следует рассматривать как нечто вроде уловки, а не как чистый дизайн, поскольку это означает, что вызывающие абоненты должны знать, что то, что у них есть, что они рассматривают как Parent-A, является В самом деле дочерним-B и, следовательно, они не должен вызывать AbstractMethodA. По сути, вы отказались от полиморфизма, который является основным преимуществом объектно-ориентированного наследования. Лично я предпочитаю делать это таким образом, чем иметь реализацию, генерирующую исключение в базовом классе, потому что тогда дочерний класс не может «случайно» вести себя плохо, «забыв» реализовать метод вообще. Он должен его реализовать, и если он реализует его так, чтобы не работать, он делает это явно. Плохая ситуация должна быть шумной.

Вы можете использовать интерфейсы. Тогда Child-A и Child-B могут реализовать разные методы и по-прежнему наследовать от Parent-A. Интерфейсы работают как абстрактные методы в том смысле, что заставляют класс реализовывать их.

Если реализация в потомках не является обязательной, вы должны выбрать 1 + 2 (т.е. пустой виртуальный метод в предке)

Если некоторые подклассы (B1, B2, ...) A используются для другого подмножества его методов, чем другие (C1, C2, ...), можно сказать, что A можно разделить на B и C.

Я не слишком хорошо знаю Delphi (совсем не :)), но я подумал, что так же, как, например, в Java и COM класс может «реализовывать» несколько интерфейсов. В C++ этого можно добиться только путем многократного наследования абстрактных классов.

Более конкретно: я бы создал два абстрактных класса (с абстрактными методами) и изменил бы дерево наследования.

Если это невозможно, обходным путем может быть «Адаптер»: промежуточный класс A_nonB_ со всеми реализованными пустыми методами B (и выдача предупреждения при их вызове) и A_nonC_. Затем измените дерево наследования, чтобы решить вашу проблему: B1, B2, ... наследуются от A_nonC_ и C1, C2, ... наследуются от A_NonB_.

Я думаю, что, вообще говоря, вы не должны наследовать от абстрактного класса, если вы не можете реализовать все абстрактные методы в первую очередь, но я понимаю, что есть некоторые ситуации, в которых все еще имеет смысл сделать это (см. класс Stream и его реализации).

Я думаю, вам следует просто создать реализации этих абстрактных методов, которые генерируют NotImplementedException.

Вы также можете попробовать использовать ObsoleteAttribute, чтобы вызов этого конкретного метода приводил к ошибке времени компиляции (конечно, помимо выброса NotImplementedException). Обратите внимание, что ObsoleteAttribute не совсем предназначен для использования для этого, но я думаю, что если вы используете значимое сообщение об ошибке с комментариями, все в порядке.

Пример обязательного кода:

[Obsolete("This class does not implement this method", true)]
public override string MyReallyImportantMethod()
{
    throw new NotImplementedException("This class does not implement this method.");
}

Другие вопросы по теме