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

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

В противном случае код является надежным и проверенным. Поэтому, если это будет только для "эстетической" причины, я пройду и оставлю пустые абстрактные классы.

Как вы думаете?

ИЗМЕНИТЬ:

1) В "пустом абстрактном классе" я имею в виду что-то вроде:

public abstract class EmptyAbstractClass {}

2) Причина "пустоты": спящий режим. Я вообще не осваиваю эту систему персистентности. Я просто понимаю, что интерфейс не может быть сопоставлен с таблицей, и по этой технической причине класс был предпочтительнее интерфейса.

Ответ 1

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

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

Ответ 2

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

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

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

Ответ 3

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

Ответ 4

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

В идеальном мире вы сможете моделировать, используя только интерфейсы. например: Автомобиль → Автомобиль → Pontiac.

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

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

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

Итак, я думаю, это вопрос вкуса.

Одно предостережение; если вы используете много проксированных классов, таких как Spring и некоторые структуры тестирования, иерархия параллельного интерфейса может привести к менее неожиданным ошибкам.

Ответ 5

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

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

На этом этапе я говорю, что играйте безопасно и живете с Ugly.

Ответ 6

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

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

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

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

Ответ 7

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

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

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

Ответ 8

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

По-видимому, конструктивное проектирование "пустой абстрактного класса" было сделано таким образом, что оно препятствует распространению класса реализации из других классов.

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

Ответ 9

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

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

abstract class AbstractFoo
    implements Fooable
{
}

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

   public void bar()
   {
   }
}

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

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

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

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

Если бы это был мой код, я бы сделал изменение им интерфейсом...

Ответ 10

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

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

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

Ответ 11

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

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