Должны ли классы-помощники/утилиты быть абстрактными?
Я часто сталкиваюсь с необходимостью извлекать общие поведения из классов в вспомогательные или утилитарные классы, которые содержат только набор статических методов. У меня возникал вопрос: следует ли объявлять такие классы абстрактными, поскольку у меня нет какой-либо разумной причины для их инстанцирования?
Каковы будут плюсы и минусы объявления такого класса абстрактным?
public [abstract] class Utilities {
public static String getSomeData() {
return "someData";
}
public static void doSomethingToObject(Object arg0) {
}
}
5 ответ(ов)
Вы можете просто объявить приватный конструктор, который ничего не делает.
Проблема с объявлением класса "абстрактным" заключается в том, что ключевое слово abstract обычно указывает на то, что класс предназначен для наследования и расширения. Это совсем не то, что вам нужно в данном случае.
Не стоит забивать себе голову абстракцией, но обязательно добавьте закрытый конструктор без параметров, чтобы предотвратить возможность инстанцирования класса.
Для тех, кто интересуется: в C# вы бы объявили класс статическим, что делает его абстрактным и запечатанным (как final в Java) в скомпилированной версии, и при этом без конструктора экземпляра вовсе. Это также приводит к ошибке компиляции при попытке объявить параметр, переменную, массив и т.д. этого типа. Удобно.
Вместо того чтобы объявлять утилитарные классы абстрактными, я предпочитаю объявлять их финальными и делать конструктор приватным. Таким образом, их нельзя будет наследовать или создавать экземпляры.
Вот пример такого класса:
public final class Utility
{
private Utility(){}
public static void doSomethingUseful()
{
// Реализация полезной функции
}
}
Этот подход гарантирует, что класс используется только как контейнер для статических методов, что и является его основной целью.
В ответ на ваш вопрос, действительно, использование приватного конструктора — это хороший шаг к предотвращению инстанцирования классов-утилит. Ваш код, представляющий класс Foo
, выглядит следующим образом:
public class Foo {
// Класс не может быть инстанцирован
private Foo() { throw new AssertionError(); }
}
Бросание AssertionError
действительно предотвращает инстанцирование класса методами внутри него, хотя они могут попытаться это сделать. В обычной ситуации это не создает проблем, но в команде вы никогда не знаете, что может сделать кто-то другой.
Что касается использования ключевого слова "abstract", я замечал случаи, когда классы утилит наследуются:
public class CoreUtils { ... }
public class WebUtils extends CoreUtils { ... }
public class Foo { ... WebUtils.someMethodInCoreUtils() ... }
Это делается для того, чтобы пользователям не приходилось запоминать, какой класс утилит включить. Однако, такой подход может иметь свои недостатки. Например, это может вызвать путаницу в иерархии классов и усложнить поддержку кода, поскольку разделение утилит по классам может быть не всегда интуитивным. Кроме того, непонятно, какая именно функциональность доступна в каждом из классов, если они не документированы должным образом.
Таким образом, можно сказать, что такой подход может являться антипаттерном. Лучше использовать статические методы в классах утилит без необходимости их наследования. Это делает код более понятным и предсказуемым для других разработчиков.
С уважением, LES
Объявляя классы абстрактными, вы фактически указываете другим разработчикам, что намерены, чтобы от этих классов наследовались другие. Действительно, вы правы, что между обычными классами и абстрактными нет большой разницы, но семантика здесь заключается именно в интерпретации вашего кода другими людьми.
Почему статические методы не могут быть абстрактными в Java?
Может ли абстрактный класс иметь конструктор?
Как мне следовало объяснить разницу между интерфейсом и абстрактным классом? [закрыто]
Инициализация ArrayList в одну строчку
Создание репозитория Spring без сущности