Являются ли интерфейсы допустимой заменой классов полезности в Java 8?
В течение последнего десятилетия или около того я использовал шаблон ниже для своих классов служебных программ Java. Класс содержит только статические методы и поля, объявляется final
, поэтому он не может быть расширен и имеет конструктор private
, поэтому он не может быть создан.
public final class SomeUtilityClass {
public static final String SOME_CONSTANT = "Some constant";
private SomeUtilityClass() {}
public static Object someUtilityMethod(Object someParameter) {
/* ... */
return null;
}
}
Теперь, с введением статических методов в интерфейсах в Java 8, я в последнее время обнаружил, что использую шаблон интерфейса утилиты:
public interface SomeUtilityInterface {
String SOME_CONSTANT = "Some constant";
static Object someUtilityMethod(Object someParameter) {
/* ... */
return null;
}
}
Это позволяет мне избавиться от конструктора и много ключевых слов (public
, static
, final
), которые неявны в интерфейсах.
Есть ли недостатки в этом подходе? Есть ли какие-либо преимущества в использовании служебного класса над служебным интерфейсом?
Ответы
Ответ 1
Исходя из того, кто придумал шаблон Constant Interface анти-шаблон, я бы сказал, хотя вы не предполагаете, что клиент реализует интерфейс, все еще возможно, возможно, проще и shouldn:
API-интерфейсы должны быть просты в использовании и трудно использовать. Легко делать простые вещи; возможно делать сложные вещи; и невозможным или, по крайней мере, трудным, делать неправильные вещи.
Хотя, как указано ниже, это действительно зависит от целевой аудитории
Множество простых в использовании шаблонов конструкций вызывает много критики (шаблон контекста, шаблон Singleton, шаблон с постоянным интерфейсом). Черт, даже принципы проектирования, такие как закон деметтера, подвергаются критике за слишком многословную.
Я бы не хотел этого говорить, но такие решения основаны на мнениях. Хотя шаблон контекста рассматривается как анти-шаблон, он проявляется в основных инфраструктурах, таких как Spring и Android SDK. Это сводится к окружающей среде, а также к целевой аудитории.
Основной недостаток, который я могу найти, указан в третьем списке под "минусами" в Constant Interface wiki:
Если в будущих выпусках требуется совместимость двоичного кода, интерфейс констант должен оставаться навсегда интерфейсом (он не может быть преобразован в класс), , хотя он не использовался в качестве интерфейса в обычном смысле.
Если вы когда-нибудь рисуете "Эй, это на самом деле не контракт, и я хочу обеспечить более сильный дизайн", вы не сможете его изменить. Но, как я уже сказал, это зависит от вас; возможно, вы не захотите изменить его в будущем.
Кроме того, ясность кода, упомянутая @TagirValeev. Интерфейсы должны быть реализованы; если вы не хотите, чтобы кто-то реализовал API, который вы поставляете, не делайте его выполнимым. Но я считаю, что это вращается вокруг заявления "целевой аудитории". Не собираюсь лгать, я с тобой на менее богатой основе, но это зависит от того, для кого мой код; не хотел бы использовать постоянный интерфейс для кода, который может быть рассмотрен.
Ответ 2
Вы должны использовать интерфейс только в том случае, если вы ожидаете, что кто-то его выполнит. Например, интерфейс java.util.stream.Stream
имеет кучу статических методов, которые могут быть расположены в некоторых классах Streams
или StreamUtils
до Java 8. Однако это действительный интерфейс, который также имеет нестатические методы и может быть реализован. java.util.Comparable
- еще один пример: все статические методы там поддерживают интерфейс. Вы не можете запрещать пользователям реализовывать свой публичный интерфейс, но для класса утилиты вы можете запретить им создавать его. Таким образом, для ясности кода я предлагаю не использовать интерфейсы, если они не предназначены для реализации.
A примечание касательно @saka1029 ответ. Хотя верно, что вы не можете определить вспомогательные частные методы и константы в одном и том же интерфейсе, не проблема создать пакет-частный класс в том же пакете, что и MyInterfaceHelper
, который будет иметь все необходимые материалы, связанные с реализацией. В общем, частные классы-пакеты хороши, чтобы скрыть детали реализации из внешнего мира.
Ответ 3
Я думаю, это сработает. Я думаю, что переменная SOME_CONSTANT по умолчанию статична в вашем SomeUtilityInterface, хотя вы этого явно не указали. Таким образом, это будет работать как Утилита, но не будет ли у вас некоторых проблем с изменчивостью, которых у вас не было бы с обычным классом, причем все переменные-члены должны быть окончательными? Пока это не проблема с вашей конкретной реализацией методов по умолчанию, я не могу придумать проблему.
Ответ 4
Нельзя использовать интерфейс.
Интерфейсы не могут иметь частные константы и статические инициализаторы.
public class Utility {
private Utility() {}
public static final Map<String, Integer> MAP_CONSTANT;
static {
Map<String, Integer> map = new HashMap<>();
map.put("zero", 0);
map.put("one", 1);
map.put("three", 3);
MAP_CONSTANT = Collections.unmodifiableMap(map);
}
private static String PRIVATE_CONSTANT = "Hello, ";
public static String hello(String name) {
return PRIVATE_CONSTANT + name;
}
}