Каков эффективный способ реализации одноэлементного шаблона в Java? [закрыто]

Ответ NilObject - хорошее начало. Ниже приведена дополнительная информация, относящаяся к ручному управлению памятью (, требуемая на iPhone ).

Если вы лично alloc/init объект, он содержит счетчик ссылок 1. Вы ответственный за очистку после него, когда он больше не нужен, либо путем вызова [foo release], либо [foo autorelease]. релиз очищает его сразу, в то время как autorelease добавляет объект в пул авторесурсов, который автоматически освободит его позже.

autorelease - это прежде всего, когда у вас есть метод, который должен вернуть объект, о котором идет речь (, чтобы вы не могли его вручную отпустить, иначе вы будете возвращать объект nil ), но вы также не хотите удерживать его.

Если вы приобрели объект, в котором вы не вызывали alloc / init, чтобы его получить - например:

foo = [NSString stringWithString:@"hello"];

, но вы хотите висеть на этом объекте, вам нужно вызвать [foo сохранить]. В противном случае, возможно, он получит autoreleased, и вы будете держаться за нулевую ссылку (как это было бы в примере выше stringWithString ). Когда вам это больше не нужно, вызовите [foo release].

780
задан Erick Robertson 9 July 2012 в 10:51
поделиться

12 ответов

Используйте перечисление:

public enum Foo {
    INSTANCE;
}

Joshua Bloch объяснил этот подход в его Эффективный Java, Перезагруженный разговор на Google I/O 2008: ссылка на видео . Также посмотрите слайды 30-32 из его представления ( effective_java_reloaded.pdf ):

Правильный Способ Реализовать сериализуемое Редактирование Singleton

public enum Elvis {
    INSTANCE;
    private final String[] favoriteSongs =
        { "Hound Dog", "Heartbreak Hotel" };
    public void printFavorites() {
        System.out.println(Arrays.toString(favoriteSongs));
    }
}

: часть онлайн "Эффективного Java" говорит:

"Этот подход функционально эквивалентен общедоступному полевому подходу, за исключением того, что это более кратко, обеспечивает оборудование для сериализации бесплатно и обеспечивает бронированную гарантию от нескольких инстанцирование, даже перед лицом сложной сериализации или отражательных нападений. В то время как этот подход должен все же быть широко принят, , одноэлементный перечислимый тип является лучшим способом реализовать одиночный элемент ".

764
ответ дан gvlasov 9 July 2012 в 10:51
поделиться

Удостоверьтесь, что Вам действительно нужен он. Сделайте Google для "одноэлементного антишаблона" для наблюдения некоторых аргументов против него. Нет ничего по сути неправильно с ним, я предполагаю, но это - просто механизм для представления некоторого глобального ресурса/данных, так удостоверьтесь, что это - лучший способ. В особенности я нашел внедрение зависимости более полезным особенно, если Вы также используете модульные тесты, потому что DI позволяет Вам использовать дразнившие ресурсы для тестирования.

47
ответ дан Jean-François Corbett 9 July 2012 в 10:51
поделиться

Если Вам не нужна ленивая загрузка, тогда просто пробуют

public class Singleton {
    private final static Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() { return Singleton.INSTANCE; }

    protected Object clone() {
        throw new CloneNotSupportedException();
    }
}

, Если Вы хотите ленивую загрузку, и Вы хотите, чтобы Ваша Singleton была ориентирована на многопотоковое исполнение, попробовала шаблон перепроверки

public class Singleton {
        private static Singleton instance = null;

        private Singleton() {}

        public static Singleton getInstance() { 
              if(null == instance) {
                  synchronized(Singleton.class) {
                      if(null == instance) {
                          instance = new Singleton();
                      }
                  }
               }
               return instance;
        }

        protected Object clone() {
            throw new CloneNotSupportedException();
        }
}

, Поскольку проверяющий дважды шаблон, как гарантируют, не будет работать (из-за некоторой проблемы с компиляторами, я не знаю ничего больше об этом.), Вы могли также попытаться синхронизировать целый getInstance-метод или создать реестр для всех Ваших Одиночных элементов.

10
ответ дан Aleksi Yrttiaho 9 July 2012 в 10:51
поделиться

Я использую Платформу Spring для управления моими одиночными элементами. Это не осуществляет "одноэлементный мыс" класса (который Вы не можете действительно сделать так или иначе, если существует несколько включенных загрузчиков класса), но обеспечивает действительно простой способ создать и настроить различные фабрики для создания различных типов объектов.

13
ответ дан Matt 9 July 2012 в 10:51
поделиться

Действительно рассмотрите, почему Вам нужен одиночный элемент прежде, чем записать его. Существуют квазирелигиозные дебаты об использовании их, которых можно довольно легко споткнуться при гуглении одиночных элементов в Java.

Лично я стараюсь избегать одиночных элементов максимально часто по многим причинам, снова большинство которых может быть найдено путем гугления одиночных элементов. Я чувствую, что довольно часто одиночными элементами злоупотребляют, потому что их легко понять всеми, они используются в качестве механизма для получения "глобальных" данных в дизайн OO, и они используются, потому что легко обойти объектное управление жизненным циклом (или действительно думающий о том, как можно сделать из B). Взгляд на вещи как Инверсия управления (IoC) или Внедрение зависимости (DI) для хорошего middleground.

при реальной необходимости в том тогда Википедия имеет хороший пример надлежащей реализации одиночного элемента.

20
ответ дан 9 July 2012 в 10:51
поделиться

Я мистифицирован некоторыми ответами, которые предлагают DI в качестве альтернативы использованию одиночных элементов; это несвязанные понятия. Можно использовать DI для введения или одиночного элемента или неодиночного элемента (например, на поток) экземпляры. По крайней мере, это верно при использовании Spring 2.x я не могу говорить за другие платформы DI.

, Таким образом, мой ответ на OP был бы (во всех кроме самого тривиального примера кода) к:

  1. Использование платформа DI как Spring, тогда
  2. Делают его частью Вашей конфигурации DI, являются ли Ваши зависимости одиночными элементами, запрашивают ограниченный по объему, ограниченная по объему сессия, или что бы то ни было.

Этот подход дает Вам разъединенное хорошее (и поэтому гибкий и тестируемый) архитектура, где, использовать ли одиночный элемент, легко обратимая деталь реализации (обеспечил любые одиночные элементы, которые Вы используете, ориентированы на многопотоковое исполнение, конечно).

21
ответ дан Andrew Swan 9 July 2012 в 10:51
поделиться

Забудьте ленивая инициализация , это слишком проблематично. Это - простое решение:

public class A {    

    private static final A INSTANCE = new A();

    private A() {}

    public static A getInstance() {
        return INSTANCE;
    }
}
90
ответ дан Jonathan 9 July 2012 в 10:51
поделиться

Решение, отправленное Stu Thompson, допустимо в Java5.0 и позже. Но я предпочел бы не использовать его, потому что я думаю, что это подвержено ошибкам.

легко забыть энергозависимый оператор и трудный понять, почему это необходимо. Без энергозависимого этот код больше не был бы ориентирован на многопотоковое исполнение из-за перепроверяемого антишаблона блокировки. Посмотрите больше об этом в абзаце 16.2.4 Параллелизм Java на практике . Короче говоря: Этот шаблон (до Java5.0 или без энергозависимого оператора) мог возвратить ссылку на объект Панели, который находится (все еще) в неправильном состоянии.

Этот шаблон был изобретен для оптимизации производительности. Но это больше - действительно не реальное беспокойство. Следующий ленивый код инициализации быстр и - что еще более важно, легче читать.

class Bar {
    private static class BarHolder {
        public static Bar bar = new Bar();
    }

    public static Bar getBar() {
        return BarHolder.bar;
    }
}
123
ответ дан Sujay 9 July 2012 в 10:51
поделиться

Ориентированный на многопотоковое исполнение в Java 5 +:

class Foo {
    private static volatile Bar bar = null;
    public static Bar getBar() {
        if (bar == null) {
            synchronized(Foo.class) {
                if (bar == null)
                    bar = new Bar(); 
            }
        }
        return bar;
    }
}
<час>

РЕДАКТИРОВАНИЕ : Обратите внимание на volatile модификатор здесь.:) Это важно, потому что без него, другие потоки, как гарантирует JMM (Модель памяти Java), не будут видеть изменений в ее значении. Синхронизация не делает , заботятся о том - она только сериализирует доступ к тому блоку кода.

РЕДАКТИРОВАНИЕ 2 : ответ @Bno детализирует подход, рекомендуемый Bill Pugh (FindBugs), и является спорным лучше. Пойдите чтение и проголосуйте за его ответ также.

94
ответ дан xyz 9 July 2012 в 10:51
поделиться

В зависимости от использования существует несколько "корректных" ответов.

С тех пор java5 лучший способ сделать это должен использовать перечисление:

public enum Foo {
   INSTANCE;
}

Пред java5, самый простой случай:

public final class Foo {

    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }

    public Object clone() throws CloneNotSupportedException{
        throw new CloneNotSupportedException("Cannot clone instance of this class");
    }
}

Позволяют нам пробежаться через код. Во-первых, Вы хотите, чтобы класс был окончательным. В этом случае я использовал final ключевое слово, чтобы позволить пользователям знать, что это является окончательным. Тогда необходимо сделать конструктора частным для предотвращения пользователей для создания их собственного Foo. Выдача исключения от конструктора предотвращает пользователей для использования отражения для создания второго Foo. Тогда Вы создаете private static final Foo поле для содержания единственного экземпляра, и public static Foo getInstance() метод для возврата его. Спецификация Java удостоверяется, что конструктора только вызывают, когда класс сначала используется.

, Когда Вы имеете очень большой объект или тяжелый код конструкции И также имеете другие доступные статические методы или поля, которые могли бы использоваться, прежде чем экземпляр необходим, тогда и только тогда необходимо использовать ленивую инициализацию.

можно использовать private static class для загрузки экземпляра. Код был бы тогда похож:

public final class Foo {

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }
}

, Так как строка private static final Foo INSTANCE = new Foo(); только выполняется, когда класс, FooLoader на самом деле используется, это заботится о ленивом инстанцировании и является им, гарантировал, что был ориентирован на многопотоковое исполнение.

, Когда Вы также хотите быть в состоянии сериализировать свой объект, необходимо удостовериться, что десериализация не создаст копию.

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

метод readResolve() удостоверится, что единственный экземпляр будет возвращен, даже когда объект был сериализирован в предыдущем выполнении Вашей программы.

227
ответ дан Roel Spilker 9 July 2012 в 10:51
поделиться
  • 1
    Я думаю, что BackgroundWorker (#5) покрывает EAP. Но, конечно, спасибо за Ваше понимание по вопросу. – Jerry Nixon - MSFT 6 September 2011 в 17:55

Иногда простое" static Foo foo = new Foo(); " недостаточно. Просто думайте о некоторой вставке основных данных, которую Вы хотите сделать.

, С другой стороны, необходимо было бы синхронизировать любой метод, который инстанцирует одноэлементной переменной как таковой. Синхронизация не плохо как такова, но она может привести к проблемам производительности или блокирующий (в очень очень редких ситуациях с помощью этого примера. Решение

public class Singleton {

    private static Singleton instance = null;

    static {
          instance = new Singleton();
          // do some of your instantiation stuff here
    }

    private Singleton() {
          if(instance!=null) {
                  throw new ErrorYouWant("Singleton double-instantiation, should never happen!");
          }
    }

    public static getSingleton() {
          return instance;
    }

}

Теперь, что происходит? Класс загружается через загрузчик класса. Непосредственно после того, как класс интерпретировался от массива байтов, VM выполняется статичный {} - блок. это - целый секрет: статический блок только называют однажды, время, данный класс (название) данного пакета загружается этим загрузчиком класса.

-3
ответ дан NullPoiиteя 9 July 2012 в 10:51
поделиться
  • 1
    Да я пытался добавить экстерна " C" вокруг функций jni (я также обновил свой код в сообщении), но тем не менее та же ошибка " сделайте: *** Никакое правило сделать цель...". расширение файла также корректно в make-файле – user745189 22 June 2011 в 05:26
Другие вопросы по тегам:

Похожие вопросы: