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

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

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

что вы думаете?

изменить :

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

public abstract class EmptyAbstractClass {}

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

11 ответов


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

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


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

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

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


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


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

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

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

в этом случае вам нужно сделать и абстрактный автомобиль.

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

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

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


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

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

на данном этапе, я говорю играть безопасно и жить с уродливым.


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

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

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

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


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

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

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


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

по-видимому," пустой абстрактный класс " Design desicion был сделан так, что он предотвращает расширение класса реализации из других классов.

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


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

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

abstract class AbstractFoo
    implements Fooable
{
}

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

   public void bar()
   {
   }
}

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

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

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

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

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


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

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

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


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

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