Представьте, что у вас есть статический метод без аргументов, который идемпотентен и всегда возвращает одно и то же значение и может выдать проверенное исключение, например, так:
class Foo {
public static Pi bar() throws Baz { getPi(); } // gets Pi, may throw
}
Теперь это хороший кандидат для ленивыхsingleton, если материал, который создает возвращенный объект, дорог и никогда не меняется.Одним из вариантов будет шаблон Holder:
class Foo {
static class PiHolder {
static final Pi PI_SINGLETON = getPi();
}
public static Pi bar() { return PiHolder.PI_SINGLETON; }
}
К сожалению, это не сработает, потому что мы не можем сгенерировать проверенное исключение из (неявного) статического блока инициализатора, поэтому вместо этого мы можем попробовать что-то подобное(при условии, что мы хотим сохранить поведение, при котором вызывающий абонент получает проверенное исключение при вызове bar()
):
class Foo {
static class PiHolder {
static final Pi PI_SINGLETON;
static {
try {
PI_SINGLETON = = getPi(); }
} catch (Baz b) {
throw new ExceptionInInitializerError(b);
}
}
public static Pi bar() throws Bar {
try {
return PiHolder.PI_SINGLETON;
} catch (ExceptionInInitializerError e) {
if (e.getCause() instanceof Bar)
throw (Bar)e.getCause();
throw e;
}
}
В этот момент, возможно, двойная проверка блокировки является более чистой?
class Foo {
static volatile Pi PI_INSTANCE;
public static Pi bar() throws Bar {
Pi p = PI_INSTANCE;
if (p == null) {
synchronized (this) {
if ((p = PI_INSTANCE) == null)
return PI_INSTANCE = getPi();
}
}
return p;
}
}
Является ли DCL анти-паттерном?Есть ли другие решения, которые я здесь упускаю (второстепенные варианты, такие как racy-одиночная проверка, также возможны, но принципиально не меняют решение)?Есть ли веская причина выбрать один из других?
Я не пробовал приведенные выше примеры, поэтому вполне возможно, что они не компилируются.
Редактировать: У меня нет роскоши повторной реализации или реструктуризации потребителей этого синглтона (т. Е. Абонентов Foo.bar()
), и при этом я не имею возможности представить структуру DI для решения этой проблемы.В основном меня интересуют ответы, которые решают проблему (предоставление синглтона с проверенными исключениями, передаваемыми вызывающей стороне) в рамках заданных ограничений.
Обновление: Я решил пойти с DCL послевсе, так как это обеспечило самый чистый способ сохранить существующий контракт, и никто не предоставил конкретную причину, почему DCL, сделанный должным образом, следует избегать.Я не использовал этот метод в принятом ответе, поскольку он казался слишком сложным способом достижения того же самого.