Я ищу кусок кода, который ведет себя немного как синглтон, но это не так (потому что синглтоны плохи :) То, что я ищу, должно соответствовать следующим целям:
- Потокобезопасный
- Простой (Понять и использовать, т. Е. Несколько строк кода. Библиотечные вызовы в порядке)
- Fast
- Не синглтон; для тестов должна быть возможность перезаписать значение (и сбросить его после теста).
- Местный (вся необходимая информация должна быть в одном месте)
- Ленивый (запускается только тогда, когда значение действительно необходимо).
- Выполнить один раз (код в RHS должен быть выполнен один раз и только один раз)
Пример кода:
private int i = runOnce(5); // Set i to 5
// Create the connection once and cache the result
private Connection db = runOnce(createDBConnection("DB_NAME"));
public void m() {
String greet = runOnce("World");
System.out.println("Hello, "+greet+"!");
}
Обратите внимание, что поля не static; только RHS (правая часть) выражения ... в некоторой степени "статичен". Тест должен иметь возможность вводить новые значения для i
и greet
временно.
Также обратите внимание, что этот фрагмент кода описывает, как я собираюсь использовать этот новый код. Не стесняйтесь заменить runOnce () на что-нибудь или переместить его в другое место (может быть, конструктор, метод init () или метод получения). Но чем меньше LOC, тем лучше.
Некоторая справочная информация:
Я не ищу Spring, я ищу кусок кода, который можно использовать для наиболее распространенного случая: вам нужно реализовать интерфейс, и второй реализации не будет, за исключением тестов, в которых Вы хотите передать в фиктивные объекты. Кроме того, Spring терпит неудачу # 2, # 3 и # 5: вам нужно выучить язык конфигурации, вы должны где-то настроить контекст приложения, ему нужен анализатор XML, и он не локальный (информация распространяется повсеместно).
Глобальный объект конфигурации или фабрика не отвечают требованиям из-за # 5.
static final
отсутствует из-за # 4 (не может изменить финал). static
пахнет из-за проблем с загрузчиком классов, но вам, вероятно, он понадобится внутри runOnce()
. Я просто предпочел бы избежать этого в LHS выражения.
Одним из возможных решений может быть использование ehcache с настройкой по умолчанию, которая возвращает тот же объект. Поскольку я могу поместить вещи в кеш, это также позволит переопределить значение в любое время. Но, возможно, есть более компактное / простое решение, чем ehcache (для которого снова требуется файл конфигурации XML и т. Д.).
[РЕДАКТИРОВАТЬ] Мне интересно, почему так много людей отрицает это. Это правильный вопрос, и случай использования довольно распространен (по крайней мере, в моем коде). Так что, если вы не понимаете вопрос (или причину, стоящую за ним), или если у вас нет ответа, или вам все равно, зачем понижать голос? : /
[EDIT2] Если вы посмотрите на контекст приложения Spring, вы обнаружите, что более 99% всех компонентов имеют только одну реализацию. Вы могли бы иметь больше, но на практике вы просто не делаете. Поэтому вместо разделения интерфейса, реализации и конфигурации я смотрю на то, что имеет только реализацию (в самом простом случае), метод current () и одну или две строки умного кода для инициализации результата для current () один раз (когда он вызывается впервые), но в то же время позволяет переопределить результат (потокобезопасен, если возможно). Думайте об этом как о атомарном «if (o == null) o = new O (); return o», где вы можете переопределить o. Может быть, класс AtomicRunOnceReference является решением.
Прямо сейчас, я просто чувствую, что то, что мы все имеем и используем ежедневно, не является оптимальным, что есть сбивающее с толку решение, которое заставит нас всех бить нас по голове и говорить «все». Так же, как мы почувствовали, когда Spring появился несколько лет назад, и мы поняли, откуда возникли все наши проблемы синглтонов и как их решить.