Действительно ли пустые абстрактные классы являются плохой практикой, и почему?

Используя GMP, можно было записать следующее:

#include <stdio.h>
#include <gmp.h>

int main() {
  mpz_t prime;
  mpz_init(prime);
  mpz_set_ui(prime, 1);
  int i;
  char* num = malloc(4000);
  for(i=0; i<10000; i++) {
    mpz_nextprime(prime, prime);
    printf("%s, ", mpz_get_str(NULL,10,prime));
  }
}

На моем MacBook Pro на 2.33 ГГц, это выполняется следующим образом:

time ./a.out > /dev/null

real    0m0.033s
user    0m0.029s
sys    0m0.003s

Вычисление 1 000 000 начал на том же ноутбуке:

time ./a.out > /dev/null

real    0m14.824s
user    0m14.606s
sys     0m0.086s

GMP высоко оптимизирован для этого вида вещи. Если Вы действительно не хотите понять алгоритмы путем записи собственного, Вам рекомендовали бы использовать libGMP под C.

10
задан Hanno Fietz 27 July 2011 в 19:20
поделиться

10 ответов

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

Пустой верхний уровень сам по себе не имеет большого значения, но я бы проверил, чтобы убедиться, что на самом деле не существует общей функциональности. Предполагая, что он действительно существует, я бы посмотрел на то, чтобы выделить общие функции в родительских классах. Я определенно буду следить за instanceof и серьезно подумаю о его рефакторинге (примеры см. В разделе «Рефакторинг» Patterns (Kerievsky)).

5
ответ дан 3 December 2019 в 13:44
поделиться

В этом случае предпочтительнее использовать интерфейсы, потому что это делает ваш код более надежным для обслуживания. То есть вы можете расширить только один класс, но можете реализовать множество интерфейсов.

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

Другими словами, если он не сломался, не исправляйте его.

18
ответ дан 3 December 2019 в 13:44
поделиться

Возникает следующий вопрос: «Что я хочу делать с этим кодом, что я не могу сделать или что мне трудно сделать, потому что там есть эти пустые абстрактные классы?» Если ответ - «ничего», оставьте их в покое. Если ответ - «что-то», возможно, стоит их удалить, но если вы можете, поговорите с людьми, которые создали их первыми, просто чтобы убедиться, что они не имеют какой-то тонкой цели. Например, возможно, ваш код использует отражение, чтобы найти все экземпляры определенного ABC и сделать с ними особые действия, и в этом случае ваш рефакторинг может нарушить код незаметными способами.

7
ответ дан 3 December 2019 в 13:44
поделиться

Это не обязательно более уродливо, чем альтернатива, которая может быть повторяющимся кодом.

В идеальном мире вы могли бы моделировать, используя только интерфейсы. например: Автомобиль -> Автомобиль -> Понтиак.

Но может быть логика, одинаковая для всех транспортных средств, поэтому интерфейс не подходит. И у вас нет логики, специфичной для Cars. Но вам действительно нужна абстракция автомобилей, потому что ваш TrafficLightController хочет различать автомобили и велосипеды.

В этом случае вам нужно создать и абстрагировать автомобиль.

Или вы можете создать интерфейс Vehicle, VehicleImpl реализует Vehicle, интерфейс Car расширяет Vehicle, интерфейс Pontiac реализует Car, а PontiacImpl реализует Pontiac extends VehicleImpl. Мне лично не нравится параллельная иерархия интерфейсов ради предотвращения пустого абстрактного класса больше, чем пустой абстрактный класс.

Так что я думаю, это дело вкуса.

Одно предостережение;

6
ответ дан 3 December 2019 в 13:44
поделиться

Задайте себе следующий вопрос: если в результате вашего рефакторинга что-то сломается в производственной среде, и ваша дальнейшая работа будет зависеть от того, насколько хорошо вы оправдываете свое решение потратить время на исправление того, чего на самом деле не было сломано, что вы скажете?

«Это было некрасиво и эстетически оскорбительно для меня» - это не тот ответ, на который я бы хотел поставить свою работу.

На этом этапе я говорю: не рискуйте и живите Уродливый.

2
ответ дан 3 December 2019 в 13:44
поделиться

Согласно теории объектно-ориентированного программирования, основной целью наследования является полиморфизм, повторное использование кода и инкапсуляция. Пустой абстрактный класс (и когда я говорю это, я имею в виду действительно пустой, без конструкторов, без методов, без свойств. Ничего!) Не достигает ни одной из трех целей, на которые рассчитывает этот метод программирования. Это эквивалентно если (истина) {...} . изменение его на интерфейс на самом деле не делает его лучше.

Если вы хотите провести рефакторинг кода, я бы посоветовал вам думать в направлении, противоположном тому, о котором вы думаете, что я имею в виду: попробуйте абстрагировать свойства, методы и конструкторы из всех классов, которые имеют общий абстрактный родительский элемент. class.

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

1
ответ дан 3 December 2019 в 13:44
поделиться

Если у вас есть следующий шаблон, вы найдете его наиболее гибким:

interface Fooable
{
   void foo();
   void bar();
}

abstract class AbstractFoo
    implements Fooable
{
}

class Foo
    extends AbstractFoo
{
   public void foo()
   {
   }

   public void bar()
   {
   }
}

Таким образом вы всегда можете пройти мимо интерфейса, но если позже вы обнаружите, что у вас есть код, который может быть общим, вы можете поместить его в абстрактный класс без необходимости вносить изменения во все классы.

Если у вас нет веская причина не использовать этот шаблон (и я подозреваю, что вы этого не сделаете), я бы предложил его использовать. Это может закончиться пустыми абстрактными классами, но я думаю, что это нормально (немного странно, но нормально).

Если в интерфейсе действительно нет методов или только один, то я бы пропустил абстрактный класс.

С точки зрения компилятора / функциональности нет реальной разницы между интерфейсом и абстрактным классом, где все методы являются абстрактный.

0
ответ дан 3 December 2019 в 13:44
поделиться

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

  1. Подождите, пока вам понадобится расширить другой класс для реальной необходимости, и вас раздражает абстрактный класс, чтобы заменить его интерфейсом.
  2. Не ждите и исправляйте дизайн.

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

Лично я, конечно, могут не согласиться, но я не люблю слишком защищаться. С такими правилами, как , если что-то не сломано, не исправляйте , вы никогда ничего не проведете рефакторингом («мои тесты пройдены, зачем мне рефакторинг?»). Замораживание кода - определенно неправильное решение, агрессивное тестирование - правильное решение .

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

Лично я, конечно, могут не согласиться, но я не люблю слишком защищаться. С такими правилами, как , если что-то не сломано, не исправляйте , вы никогда ничего не проведете рефакторингом («мои тесты пройдены, зачем мне рефакторинг?»). Замораживание кода определенно не является правильным решением, агрессивное тестирование - правильное решение .

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

Лично я, конечно, могут не согласиться, но я не люблю слишком защищаться. С такими правилами, как , если что-то не сломано, не исправляйте , вы никогда не проведете рефакторинг («мои тесты пройдены, зачем мне рефакторинг?»). Замораживание кода - определенно неправильное решение, агрессивное тестирование - правильное решение .

не вижу, что может сломаться, поэтому я бы пошел на это.

Лично я, конечно, могут не согласиться, но я не люблю слишком защищаться. С такими правилами, как , если что-то не сломано, не исправляйте , вы никогда не проведете рефакторинг («мои тесты пройдены, зачем мне рефакторинг?»). Замораживание кода определенно не является правильным решением, агрессивное тестирование - правильное решение .

не вижу, что может сломаться, поэтому я бы пошел на это.

Лично я, конечно, могут не согласиться, но я не люблю слишком защищаться. С такими правилами, как , если что-то не сломано, не исправляйте , вы никогда не проведете рефакторинг («мои тесты пройдены, зачем мне рефакторинг?»). Замораживание кода определенно не является правильным решением, агрессивное тестирование - правильное решение .

2
ответ дан 3 December 2019 в 13:44
поделиться

Ключевым моментом является то, что вы можете расширить только один абстрактный класс, в то время как вы можете реализовать больше интерфейсов.

Очевидно, проектное решение «пустой абстрактный класс» было принято таким образом, чтобы он не позволял реализующему классу расширяться из других классов.

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

1
ответ дан 3 December 2019 в 13:44
поделиться

Возникает очевидный вопрос: почему это было помещено туда? и Как это используется?

Я предполагаю, что либо (а) Это след какой-то фальстарта. В раннем черновике были некоторые данные или функции в абстрактном классе, но по мере работы программиста все они перемещались в другое место, пока не осталось ничего, кроме пустой оболочки. Но более вероятно (б) В какой-то момент есть код, который говорит «if (x instanceof ...». Я думаю, что это плохой дизайн, в основном использующий членство в классе в качестве флага. Если вам нужен флаг, создайте флаг, не притворяйтесь, что вы «более объектно-ориентированы», создавая класс или интерфейс, а затем используя его как флаг. Это может соответствовать определению лучшего кодирования из учебников, но на самом деле просто делает код более запутанным.

+1 к Monachus за указание, что пустой интерфейс не лучше, чем пустой абстрактный класс. Да, да, я знаю, что Sun сделали это сами с помощью Cloneable, но я думаю, что это было неудачное решение проблемы.

0
ответ дан 3 December 2019 в 13:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: