За последние несколько месяцев я несколько раз спотыкался о следующей технике / схеме. Тем не менее, я не могу найти конкретное имя, и я не уверен на 100% во всех его преимуществах и недостатках.
Шаблон идет следующим образом:
В интерфейсе Java набор общих методов определяется как обычно. Однако при использовании внутреннего класса экземпляр по умолчанию пропускается через интерфейс.
public interface Vehicle {
public void accelerate();
public void decelerate();
public static class Default {
public static Vehicle getInstance() {
return new Car(); // or use Spring to retrieve an instance
}
}
}
Мне кажется, что самое большое преимущество заключается в том, что разработчику нужно знать только об интерфейсе, а не о его реализациях, например, в случае, если он хочет быстро создать экземпляр.
Vehicle someVehicle = Vehicle.Default.getInstance();
someVehicle.accelerate();
Кроме того, я видел, как этот метод используется вместе с Spring для динамического предоставления экземпляров в зависимости от конфигурации. В связи с этим, похоже, что это может помочь с модульностью.
Тем не менее, я не могу избавиться от ощущения, что это неправильное использование интерфейса, поскольку он связывает интерфейс с одной из его реализаций. (Принцип инверсии зависимостей и т. Д.). Может ли кто-нибудь объяснить мне, как называется этот метод, а также его достоинства и недостатки?
Обновить:
После некоторого времени для рассмотрения я перепроверил и заметил, что следующая одноэлементная версия шаблона использовалась гораздо чаще. В этой версии общедоступный статический экземпляр предоставляется через интерфейс, который инициализируется только один раз (из-за того, что поле является окончательным). Кроме того, экземпляр почти всегда извлекается с использованием Spring или универсальной фабрики, которая отделяет интерфейс от реализации.
public interface Vehicle {
public void accelerate();
public void decelerate();
public static class Default {
public static final Vehicle INSTANCE = getInstance();
private static Vehicle getInstance() {
return new Car(); // or use Spring/factory here
}
}
}
// Which allows to retrieve a singleton instance using...
Vehicle someVehicle = Vehicle.Default.INSTANCE;
В двух словах: кажется, что это пользовательский шаблон синглтона / фабрики, который в основном позволяет представить экземпляр или синглтон через его интерфейс. Что касается недостатков, некоторые из них были названы в ответах и комментариях ниже. Пока что преимущество заключается в его удобстве.
Vehicle.Default
следует перенести в пространство имен пакета как фабричный класс, напримерVehicleFactory
.Vehicle.Default.getInstance() != Vehicle.Default.getInstance()
Ответы:
Default.getInstance
ИМХО - это просто очень специфическая форма фабричного метода , смешанного с соглашением об именах, взятым из шаблона синглтона (но не являющегося реализацией последнего). В текущей форме это является нарушением « принципа единой ответственности », потому что интерфейс (который уже служит для объявления API класса) берет на себя дополнительную ответственность за предоставление экземпляра по умолчанию, который был бы намного лучше помещен в отдельныйVehicleFactory
учебный класс.Основная проблема, вызванная этой конструкцией, заключается в том, что она вызывает циклическую зависимость между
Vehicle
иCar
. Например, в текущей форме не удастся разместитьVehicle
в одной библиотеке, аCar
в другой. Использование отдельного класса фабрики и размещение тамDefault.getInstance
метода решит эту проблему. Также может быть хорошей идеей дать этому методу другое имя, чтобы избежать путаницы, связанной с одиночками. Таким образом, результат может быть что-то вродеVehicleFactory.createDefaultInstance()
источник
VehicleFactory
является более традиционным, чем вложенная конструкцияVehicle.Default
, особенно с вводящим в заблуждение методом getInstance. Хотя с помощью Spring можно обойти циклическую зависимость, я лично предпочел бы первый вариант только из-за здравого смысла. И, тем не менее, у вас остается возможность перенести фабрику на другой компонент, затем изменить реализацию для работы без Spring и т. Д.autodrive()
метод. Ваш очень универсальный Автомобиль должен затем измениться, чтобы реализовать этот метод, например, с помощью исключения NotImplementedException, но это не дает дополнительной причины для изменения в Транспортном средстве.Это похоже на нулевой объект шаблон для меня.
Но это сильно зависит от того, как
Car
реализован класс. Если он реализован «нейтрально», то это определенно Нулевой Объект. Это означает, что если вы можете удалить все нулевые проверки на интерфейсе и поместить экземпляр этого класса вместо каждого вхожденияnull
, то код все равно должен работать правильно.Также, если это именно тот шаблон, то нет проблемы создания тесной связи между самим интерфейсом и реализацией нулевого объекта. Потому что нулевой объект может быть везде, где используется указанный интерфейс.
источник