Реализации по умолчанию абстрактных методов - PullRequest
2 голосов
/ 28 ноября 2008

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

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

Должен ли я

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

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

Ответы [ 6 ]

9 голосов
/ 28 ноября 2008

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

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

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

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

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

2 голосов
/ 28 ноября 2008

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

1 голос
/ 28 ноября 2008

Я думаю, что, вообще говоря, вы не должны наследовать от абстрактного класса, если вы не можете реализовать все абстрактные методы в первую очередь, но я понимаю, что в некоторых ситуациях все еще имеет смысл это делать , (см. класс 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.");
}
0 голосов
/ 28 ноября 2008

Если некоторые подклассы (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 _.

0 голосов
/ 28 ноября 2008

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

0 голосов
/ 28 ноября 2008

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

...