Singleton com argumentos em Java

142

Eu estava lendo o artigo de Singleton na Wikipedia e me deparei com este exemplo:

public class Singleton {
    // Private constructor prevents instantiation from other classes
    private Singleton() {}

    /**
     * SingletonHolder is loaded on the first execution of Singleton.getInstance() 
     * or the first access to SingletonHolder.INSTANCE, not before.
     */
    private static class SingletonHolder { 
        private static final Singleton INSTANCE = new Singleton();
    }

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

Embora eu realmente goste da maneira como esse Singleton se comporta, não consigo ver como adaptá-lo para incorporar argumentos ao construtor. Qual é a maneira preferida de fazer isso em Java? Eu teria que fazer algo assim?

public class Singleton
{
    private static Singleton singleton = null;  
    private final int x;

    private Singleton(int x) {
        this.x = x;
    }

    public synchronized static Singleton getInstance(int x) {
        if(singleton == null) singleton = new Singleton(x);
        return singleton;
    }
}

Obrigado!


Edit: Eu acho que comecei uma tempestade de controvérsia com o meu desejo de usar Singleton. Deixe-me explicar minha motivação e espero que alguém possa sugerir uma idéia melhor. Estou usando uma estrutura de computação em grade para executar tarefas em paralelo. Em geral, eu tenho algo parecido com isto:

// AbstractTask implements Serializable
public class Task extends AbstractTask
{
    private final ReferenceToReallyBigObject object;

    public Task(ReferenceToReallyBigObject object)
    {
        this.object = object;
    }

    public void run()
    {
        // Do some stuff with the object (which is immutable).
    }
}

O que acontece é que, embora eu apenas passe uma referência aos meus dados para todas as tarefas, quando as tarefas são serializadas, os dados são copiados repetidamente. O que eu quero fazer é compartilhar o objeto entre todas as tarefas. Naturalmente, eu posso modificar a classe da seguinte maneira:

// AbstractTask implements Serializable
public class Task extends AbstractTask
{
    private static ReferenceToReallyBigObject object = null;

    private final String filePath;

    public Task(String filePath)
    {
        this.filePath = filePath;
    }

    public void run()
    {
        synchronized(this)
        {
            if(object == null)
            {
                ObjectReader reader = new ObjectReader(filePath);
                object = reader.read();
            }
        }

        // Do some stuff with the object (which is immutable).
    }
}

Como você pode ver, mesmo aqui, tenho o problema de que a passagem de um caminho de arquivo diferente não significa nada após a passagem do primeiro. É por isso que gosto da ideia de uma loja que foi postada nas respostas. De qualquer forma, em vez de incluir a lógica para carregar o arquivo no método run, eu queria abstrair essa lógica em uma classe Singleton. Não darei mais outro exemplo, mas espero que você entenda. Deixe-me ouvir suas idéias para uma maneira mais elegante de realizar o que estou tentando fazer. Mais uma vez obrigado!


fonte
1
O padrão de fábrica é o que você deseja. Idealmente, as tarefas de grade devem ser completamente independentes de qualquer outra coisa e receber todos os dados necessários para executar e retornar seus resultados. No entanto, essa nem sempre é a solução mais viável; portanto, serializar os dados em um arquivo não é uma má idéia. Eu acho que a coisa toda singleton é um pouco de arenque; você não quer um singleton.
23610 oxbow_lakes
2
É uma pena que você tenha usado o termo Singleton que acompanha essa bagagem. O termo adequado para esse padrão é Interning, na verdade. Interning é um método para garantir que os valores abstratos sejam representados por apenas uma instância. O uso interno de string é o uso mais comum: en.wikipedia.org/wiki/String_intern_pool.
Notnoop 27/06/2009
Você pode dar uma olhada no Terracotta. Ele mantém a identidade do objeto no cluster. Quando você envia uma referência aos dados que já estão no cluster, eles não são serializados novamente.
Taylor Gautier
21
Deixando de lado a questão de saber se o padrão singleton deve ser usado, observe que quase todas as respostas aqui parecem assumir que o objetivo de fornecer um argumento é permitir a criação de "vários singletons" diferenciados pelo valor do referido parâmetro. Mas outro objetivo possível é fornecer acesso a um objeto externo que é o único objeto desse tipo que a instância exclusiva da classe singleton precisará. Portanto, precisamos distinguir um parâmetro fornecido para esse acesso de um parâmetro destinado a criar "várias instâncias singleton".
23412 Carl
2
Outro cenário para um "singleton com parâmetros": um aplicativo da Web que criará seu singleton imutável exclusivo com base nas informações fornecidas com a primeira solicitação futura (thread). O domínio do pedido poderia determinar o comportamento de alguns singleton por exemplo
fustaki

Respostas:

171

Vou deixar meu argumento bem claro: um singleton com parâmetros não é um singleton .

Um singleton, por definição, é um objeto que você deseja instanciar não mais que uma vez. Se você está tentando alimentar parâmetros para o construtor, qual é o objetivo do singleton?

Você tem duas opções. Se você deseja que seu singleton seja inicializado com alguns dados, você pode carregá-lo com dados após a instanciação , da seguinte maneira:

SingletonObj singleton = SingletonObj.getInstance();
singleton.init(paramA, paramB); // init the object with data

Se a operação que seu singleton está executando for recorrente e com parâmetros diferentes a cada vez, você também pode passar os parâmetros para o método principal que está sendo executado:

SingletonObj singleton = SingletonObj.getInstance();
singleton.doSomething(paramA, paramB); // pass parameters on execution

Em qualquer caso, a instanciação será sempre sem parâmetros. Caso contrário, seu singleton não é um singleton.

Yuval Adam
fonte
1
+1 É assim que eu provavelmente faria ao codificar. Em C #, eu apenas usaria propriedades. Java, provavelmente assim.
Zack
131
desculpe, isso não é verdade. há situações em que você precisa passar parâmetros dinamicamente criados que permanecem os mesmos para o tempo de execução do aplicativo de furo. então você não pode usar uma constante no singleton, mas precisa passar essa constante quando ele é criado. depois de passado, é a mesma constante para o tempo do furo. um setter não fará o trabalho se você precisar dessa constante específica dentro do construtor.
masi
1
@masi, como diz o autor - não é um singleton. Se você precisa passar dinamicamente constantemente, pode ser necessário criar muitas dessas classes com constantes diferentes. Portanto, não há sentido em singleton.
Dmitry Zaytsev
53
Se você precisa apenas de uma instância de uma classe durante toda a vida útil de um aplicativo, mas precisa fornecer um valor à instância no momento da inicialização, por que isso não é mais um singleton?
Oscar
4
"Se você está tentando alimentar parâmetros para o construtor, qual é o objetivo do singleton?" - Pode-se também dizer: "Se você criar todo o aplicativo em uma instância única, qual é o argumento dos argumentos da linha de comando?", E a resposta é que isso faz muito sentido. Agora, pode-se dizer que isso é bastante diferente de uma classe singleton, exceto se a classe for realmente a classe Main que recebe os args [] do método principal - então é a mesma coisa. O argumento final, que pode simplesmente permanecer, é que esta é uma situação bastante excepcional.
Presidente da Dreamspace
41

Eu acho que você precisa de algo como um fábrica para ter objetos com vários parâmetros instanciados e reutilizados. Ele pode ser implementado usando um parâmetro sincronizado HashMapou ConcurrentHashMapmapeado ( Integerpor exemplo) para sua classe parametrizável 'singleton'.

Embora você possa chegar ao ponto em que deve usar classes regulares e não singleton (por exemplo, precisando de 10.000 singleton com parâmetros diferentes).

Aqui está um exemplo para essa loja:

public final class UsefulObjFactory {

    private static Map<Integer, UsefulObj> store =
        new HashMap<Integer, UsefulObj>();

    public static final class UsefulObj {
        private UsefulObj(int parameter) {
            // init
        }
        public void someUsefulMethod() {
            // some useful operation
        }
    }

    public static UsefulObj get(int parameter) {
        synchronized (store) {
            UsefulObj result = store.get(parameter);
            if (result == null) {
                result = new UsefulObj(parameter);
                store.put(parameter, result);
            }
            return result;
        }
    }
}

Para avançar ainda mais, os Java enums também podem ser considerados (ou usados ​​como) singletons parametrizados, embora permitindo apenas um número fixo de variantes estáticas.

No entanto, se você precisar de uma solução distribuída 1 , considere alguma solução de armazenamento em cache lateral. Por exemplo: EHCache, Terracota, etc.

1 no sentido de estender várias VMs em provavelmente vários computadores.

akarnokd
fonte
Sim, é exatamente disso que eu preciso. Muito obrigado! Concordo que a maneira como lidava com argumentos no meu exemplo não fazia muito sentido, mas não pensei nisso. Veja minha explicação nos comentários da resposta de oxbow_lakes.
1
Este não é um singleton; agora você tem mais de um deles. LOL
oxbow_lakes 26/06/09
@ Scott: Eu sugiro algo parecido com o que Yuval sugeriu abaixo. Faz um pouco mais de sentido e você tem um singleton "verdadeiro". editar
Zack
Espero que ninguém se importe comigo editando os nomes no código; Eu posso imaginar que isso é realmente confuso para iniciantes. Reversão se você discordar
oxbow_lakes
Sim, poderíamos chamá-los de Multitron e ainda alcançar o mesmo objetivo que o OP queria em primeiro lugar IMHO.
26610 akarnokd
22

Você pode adicionar um método de inicialização configurável para separar a instanciação da obtenção.

public class Singleton {
    private static Singleton singleton = null;
    private final int x;

    private Singleton(int x) {
        this.x = x;
    }

    public static Singleton getInstance() {
        if(singleton == null) {
            throw new AssertionError("You have to call init first");
        }

        return singleton;
    }

    public synchronized static Singleton init(int x) {
        if (singleton != null)
        {
            // in my opinion this is optional, but for the purists it ensures
            // that you only ever get the same instance when you call getInstance
            throw new AssertionError("You already initialized me");
        }

        singleton = new Singleton(x);
        return singleton;
    }

}

Em seguida, você pode ligar Singleton.init(123)uma vez para configurá-lo, por exemplo, na inicialização do aplicativo.

miguel
fonte
13

Você também pode usar o padrão Builder se desejar mostrar que alguns parâmetros são obrigatórios.

    public enum EnumSingleton {

    INSTANCE;

    private String name; // Mandatory
    private Double age = null; // Not Mandatory

    private void build(SingletonBuilder builder) {
        this.name = builder.name;
        this.age = builder.age;
    }

    // Static getter
    public static EnumSingleton getSingleton() {
        return INSTANCE;
    }

    public void print() {
        System.out.println("Name "+name + ", age: "+age);
    }


    public static class SingletonBuilder {

        private final String name; // Mandatory
        private Double age = null; // Not Mandatory

        private SingletonBuilder(){
          name = null;
        }

        SingletonBuilder(String name) {
            this.name = name;
        }

        public SingletonBuilder age(double age) {
            this.age = age;
            return this;
        }

        public void build(){
            EnumSingleton.INSTANCE.build(this);
        }

    }


}

Então você pode criar / instanciar / parametrizar da seguinte maneira:

public static void main(String[] args) {
    new EnumSingleton.SingletonBuilder("nico").age(41).build();
    EnumSingleton.getSingleton().print();
}
gerardnico
fonte
6

A instrução " um singleton com parâmetros não é um singleton " não está completamente correta . Precisamos analisar isso da perspectiva do aplicativo e não da perspectiva do código.

Construímos a classe singleton para criar uma única instância de um objeto em uma execução de aplicativo. Ao ter um construtor com o parâmetro, você pode criar flexibilidade no seu código para alterar alguns atributos do seu objeto singleton toda vez que você executa o aplicativo. Isso não é uma violação do padrão Singleton. Parece uma violação se você ver isso da perspectiva do código.

Os Padrões de Design estão lá para nos ajudar a escrever código flexível e extensível, para não nos impedir de escrever um bom código.

Vinod Nalla
fonte
12
Esta não é uma resposta para a pergunta do OP, deve ser um comentário.
Thierry J.
5

Use getters e setters para definir a variável e tornar o construtor padrão privado. Então use:

Singleton.getInstance().setX(value);
AlbertoPL
fonte
1
Não entenda por que isso foi rejeitado. É uma resposta válida tbh. : /
Zack
13
Porque é uma resposta inútil. Por exemplo, imagine um sistema em que o nome de usuário e a senha iniciais do administrador inicial sejam argumentos construtores. Agora, se eu fizer disso um singleton e fizer o que você diz, recebo getters e setters para o administrador, o que não é exatamente o que você deseja. Portanto, embora sua opção possa ser válida em alguns casos, ela realmente não responde ao caso geral que foi a pergunta. (sim, estou trabalhando no sistema que descrevi e não, eu não usaria um padrão singleton se não fosse pelo fato de a tarefa indicar "use um padrão singleton aqui")
Jasper
5

Surpreso que ninguém tenha mencionado como um criador de logs é criado / recuperado. Por exemplo, abaixo mostra como o log4J logger é recuperado.

// Retrieve a logger named according to the value of the name parameter. If the named logger already exists, then the existing instance will be returned. Otherwise, a new instance is created.
public static Logger getLogger(String name)

Existem alguns níveis de indireções, mas a parte principal é o método abaixo , que praticamente conta tudo sobre como funciona. Ele usa uma tabela de hash para armazenar os registradores existentes e a chave é derivada do nome. Se o criador de logs não existir para um nome, ele usa uma fábrica para criar o criador de logs e o adiciona à tabela de hash.

69   Hashtable ht;
...
258  public
259  Logger getLogger(String name, LoggerFactory factory) {
260    //System.out.println("getInstance("+name+") called.");
261    CategoryKey key = new CategoryKey(name);
262    // Synchronize to prevent write conflicts. Read conflicts (in
263    // getChainedLevel method) are possible only if variable
264    // assignments are non-atomic.
265    Logger logger;
266
267    synchronized(ht) {
268      Object o = ht.get(key);
269      if(o == null) {
270        logger = factory.makeNewLoggerInstance(name);
271        logger.setHierarchy(this);
272        ht.put(key, logger);
273        updateParents(logger);
274        return logger;
275      } else if(o instanceof Logger) {
276        return (Logger) o;
277      } 
...
wanghq
fonte
4

Modificação do padrão Singleton que usa o idioma de titular da demanda de inicialização de Bill Pugh . Isso é seguro para threads sem a sobrecarga de construções de idiomas especializadas (ou seja, voláteis ou sincronizadas):

public final class RInterfaceHL {

    /**
     * Private constructor prevents instantiation from other classes.
     */
    private RInterfaceHL() { }

    /**
     * R REPL (read-evaluate-parse loop) handler.
     */
    private static RMainLoopCallbacks rloopHandler = null;

    /**
     * SingletonHolder is loaded, and the static initializer executed, 
     * on the first execution of Singleton.getInstance() or the first 
     * access to SingletonHolder.INSTANCE, not before.
     */
    private static final class SingletonHolder {

        /**
         * Singleton instance, with static initializer.
         */
        private static final RInterfaceHL INSTANCE = initRInterfaceHL();

        /**
         * Initialize RInterfaceHL singleton instance using rLoopHandler from
         * outer class.
         * 
         * @return RInterfaceHL instance
         */
        private static RInterfaceHL initRInterfaceHL() {
            try {
                return new RInterfaceHL(rloopHandler);
            } catch (REngineException e) {
                // a static initializer cannot throw exceptions
                // but it can throw an ExceptionInInitializerError
                throw new ExceptionInInitializerError(e);
            }
        }

        /**
         * Prevent instantiation.
         */
        private SingletonHolder() {
        }

        /**
         * Get singleton RInterfaceHL.
         * 
         * @return RInterfaceHL singleton.
         */
        public static RInterfaceHL getInstance() {
            return SingletonHolder.INSTANCE;
        }

    }

    /**
     * Return the singleton instance of RInterfaceHL. Only the first call to
     * this will establish the rloopHandler.
     * 
     * @param rloopHandler
     *            R REPL handler supplied by client.
     * @return RInterfaceHL singleton instance
     * @throws REngineException
     *             if REngine cannot be created
     */
    public static RInterfaceHL getInstance(RMainLoopCallbacks rloopHandler)
            throws REngineException {
        RInterfaceHL.rloopHandler = rloopHandler;

        RInterfaceHL instance = null;

        try {
            instance = SingletonHolder.getInstance();
        } catch (ExceptionInInitializerError e) {

            // rethrow exception that occurred in the initializer
            // so our caller can deal with it
            Throwable exceptionInInit = e.getCause();
            throw new REngineException(null, exceptionInInit.getMessage());
        }

        return instance;
    }

    /**
     * org.rosuda.REngine.REngine high level R interface.
     */
    private REngine rosudaEngine = null;

    /**
     * Construct new RInterfaceHL. Only ever gets called once by
     * {@link SingletonHolder.initRInterfaceHL}.
     * 
     * @param rloopHandler
     *            R REPL handler supplied by client.
     * @throws REngineException
     *             if R cannot be loaded.
     */
    private RInterfaceHL(RMainLoopCallbacks rloopHandler)
            throws REngineException {

        // tell Rengine code not to die if it can't
        // load the JRI native DLLs. This allows
        // us to catch the UnsatisfiedLinkError
        // ourselves
        System.setProperty("jri.ignore.ule", "yes");

        rosudaEngine = new JRIEngine(new String[] { "--no-save" }, rloopHandler);
    }
}
tekumara
fonte
Eu acho que seria uma boa idéia para finally { RInterfaceHL.rloopHandler = null; }em getInstance, porque essa referência estática pode causar um vazamento de memória, se não formos cuidadosos. No seu caso, parece que não é um problema, mas eu poderia imaginar um cenário em que o objeto passado é grande e usado apenas pelo RInterfaceHLctor para obter alguns valores e não para manter uma referência a ele.
TWIStErRob
Idéia: return SingletonHolder.INSTANCEfuncionaria tão bem quanto getInstance. Não acho que haja uma necessidade de encapsulamento aqui, porque a classe externa já conhece as entranhas da classe interna, elas estão fortemente acopladas: sabe que rloopHandlerprecisa de init antes de chamar. Além disso, o construtor privado não tem efeito, porque o material privado da classe interna está simplesmente disponível para a classe externa.
TWiStErRob
1
Link quebrado. Você estava se referindo a en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom ?
Jorge Lavín
3

A razão pela qual você não consegue entender como realizar o que está tentando fazer é provavelmente que o que está tentando fazer realmente não faz sentido. Você quer chamar getInstance(x)com argumentos diferentes, mas sempre retorna o mesmo objeto? Qual é o comportamento que você deseja quando liga getInstance(2)e depois getInstance(5)?

Se você deseja que o mesmo objeto, mas que seu valor interno seja diferente, que é a única maneira de ainda ser um singleton, não precisará se preocupar com o construtor; você acabou de definir o valor na getInstance()saída do objeto. Obviamente, você entende que todas as suas outras referências ao singleton agora têm um valor interno diferente.

Se você deseja getInstance(2)e getInstance(5)retorna objetos diferentes, por outro lado, não está usando o padrão Singleton, está usando o padrão Factory.

caos
fonte
3

No seu exemplo, você não está usando um singleton. Observe que se você fizer o seguinte (assumindo que o Singleton.getInstance seja realmente estático):

Singleton obj1 = Singleton.getInstance(3);
Singleton obj2 = Singleton.getInstance(4);

Então os valores do obj2.x são 3, não 4. Se você precisar fazer isso, torne-a uma classe simples. Se o número de valores for pequeno e fixo, você pode considerar o uso de umenum . Se você estiver tendo problemas com a geração excessiva de objetos (o que geralmente não é o caso), considere os valores de armazenamento em cache (e verifique as fontes ou obtenha ajuda com isso, pois é óbvio como criar caches sem o perigo de vazamento de memória).

Você também pode ler este artigo, pois os singletons podem ser usados ​​com muita facilidade.

Kathy Van Stone
fonte
3

Outra razão pela qual os Singletons são um antipadrão é que, se escritos de acordo com as recomendações, com construtor privado, são muito difíceis de subclassificar e configurar para usar em determinados testes de unidade. Seria necessário manter o código legado, por exemplo.

JosefB
fonte
3

Se você deseja criar uma classe Singleton servindo como um contexto, uma boa maneira é ter um arquivo de configuração e ler os parâmetros do arquivo dentro da instância ().

Se os parâmetros que alimentam a classe Singleton forem obtidos dinamicamente durante a execução do seu programa, basta usar um HashMap estático armazenando instâncias diferentes na sua classe Singleton para garantir que, para cada parâmetro, apenas uma instância seja criada.

user3025839
fonte
1

Isso não é um singleton, mas pode ser algo que pode resolver seu problema.

public class KamilManager {

  private static KamilManager sharedInstance;

  /**
   * This method cannot be called before calling KamilManager constructor or else
   * it will bomb out.
   * @return
   */
  public static KamilManager getInstanceAfterInitialized() {
    if(sharedInstance == null)
        throw new RuntimeException("You must instantiate KamilManager once, before calling this method");

    return sharedInstance;
}

  public KamilManager(Context context, KamilConfig KamilConfig) {
    //Set whatever you need to set here then call:
  s  haredInstance = this;
  }
}
Kamilski81
fonte
1

Se considerarmos o problema como "como fazer singleton com estado", não será necessário passar o estado como parâmetro construtor. Concordo com as postagens que inicializam os estados ou usando o método set depois de obter a instância singleton.

Outra pergunta é: é bom ter singleton com estado?

user3014901
fonte
1

Não poderíamos fazer algo assim:

public class Singleton {

    private int x;

    // Private constructor prevents instantiation from other classes
    private Singleton() {}

    /**
     * SingletonHolder is loaded on the first execution of Singleton.getInstance() 
     * or the first access to SingletonHolder.INSTANCE, not before.
     */
    private static class SingletonHolder { 
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance(int x) {
        Singleton instance = SingletonHolder.INSTANCE;
        instance.x = x;
        return instance;
    }
}
Ionut Negru
fonte
1

Apesar do que alguns possam afirmar, aqui está um singleton com parâmetros no construtor

public class Singleton {

    private static String aParameterStored;

    private static final Singleton instance = new Singleton("Param to set");

    private Singleton() {
        // do nothing
    }

    private Singleton(String param) {
        aParameterStored = param;
    }

    public static Singleton getInstance() {
        return instance;
    }

    /*
     * ... stuff you would like the singleton do
     */
}

O padrão singleton diz:

  • garantir que apenas uma instância da classe singleton exista
  • fornecer acesso global a essa instância.

que são respeitados com este exemplo.

Por que não definir diretamente a propriedade? É o caso do livro didático para mostrar como podemos obter um singleton com construtor com parâmetro, mas pode ser útil em algumas situações. Por exemplo, em casos de herança para forçar o singleton a definir algumas propriedades da superclasse.

Zou
fonte
0

Estou com medo de postar isso como resposta, mas não entendo por que ninguém pensa sobre isso, talvez essa resposta também já tenha sido dada e eu simplesmente não entendi.

public class example  {
    private volatile static example instance;

    private String string;
    private int iInt = -1; //any number you know you don't want to use here

  private example() {

    //In case someone uses the private method to create a new Instance
    if (instance != null){
      throw new RuntimeException("Use getInstance() method to get the single instance of this class.");
    }
  }

  public synchronized static example getIsntance(){
    if(instance == null){
      instance = new example();
    }
    return instance;
  }

public void methodDoingWork(){
    if(checkInit()){
      //DoSome
    }
  }

  private boolean checkInit(){
    boolean filled = (this.string != null) && (this.iInt != -1);
    return filled;
  }

  public void setString(String string) {
    if(this.string == null){
      this.string = string;
    }else{
      throw new RuntimeException("You try to override an already setValue"); 
    }
  }

  public void setiInt(int iInt) {
    if(this.iInt == -1){
      this.iInt = iInt;
    }else{
      throw new RuntimeException("You try to override an already setValue");
    }
  }
}

Como os getInstance()retornos sempre são a mesma instância, acho que isso poderia funcionar. Se isso estiver errado demais, vou excluí-lo, apenas estou interessado neste tópico.

HydroHeiperGen
fonte
-1

Eu acho que isso é um problema comum. Separar a "inicialização" do singleton do "get" do singleton pode funcionar (este exemplo usa uma variação do bloqueio verificado duas vezes).

public class MySingleton {

    private static volatile MySingleton INSTANCE;

    @SuppressWarnings("UnusedAssignment")
    public static void initialize(
            final SomeDependency someDependency) {

        MySingleton result = INSTANCE;

        if (result != null) {
            throw new IllegalStateException("The singleton has already "
                    + "been initialized.");
        }

        synchronized (MySingleton.class) {
            result = INSTANCE;

            if (result == null) {
                INSTANCE = result = new MySingleton(someDependency);
            } 
        }
    }

    public static MySingleton get() {
        MySingleton  result = INSTANCE;

        if (result == null) {
            throw new IllegalStateException("The singleton has not been "
                    + "initialized. You must call initialize(...) before "
                    + "calling get()");
        }

       return result;
    }

    ...
}
Michael Andrews
fonte
Sempre poderia retornar "resultado" no método de inicialização também, suponho.
Michael Andrews
-2

Singleton é, obviamente, um "antipadrão" (assumindo uma definição de estática com estado variável).

Se você deseja um conjunto fixo de objetos de valor imutável, as enumerações são o caminho a seguir. Para um conjunto grande e possivelmente aberto de valores, você pode usar um Repositório de alguma forma - geralmente com base em uma Mapimplementação. Obviamente, quando você estiver lidando com estática, tenha cuidado com o encadeamento (sincronize suficientemente o suficiente ou use uma ConcurrentMapverificação para verificar se outro encadeamento não o venceu ou use algum tipo de futuro).

Tom Hawtin - linha de orientação
fonte
4
Somente um antipadrão se usado incorretamente, embora essa seja a definição de um antipadrão. Só porque você os viu onde eles não pertenciam no passado não significa que eles não têm um lugar.
geowa4
O uso correto de um singleton é demonstrar código incompetente.
Tom Hawtin - tackline
-6

Singletons são geralmente considerados anti-padrões e não devem ser usados. Eles não facilitam o teste de código.

Um singleton com um argumento não faz sentido de qualquer maneira - o que aconteceria se você escrevesse:

Singleton s = SingletonHolder.getInstance(1);
Singleton t = SingletonHolder.getInstance(2); //should probably throw IllegalStateException

Seu singleton também não é seguro para threads, pois vários threads podem fazer chamadas simultâneas, getInstanceresultando na criação de mais de uma instância (possivelmente com valores diferentes de x).

oxbow_lakes
fonte
Isso é bastante discutível.
214 AlbertoPL
1
Sim, é discutível; daí o meu uso da palavra "geralmente". Eu acho que é justo dizer que eles são geralmente considerados uma má idéia
oxbow_lakes
É discutível - algumas pessoas afirmam que o que é chamado de "anti-padrões" se encaixa na definição de padrões, mas é apenas um padrão ruim.
Tom Hawtin - tackline 26/06/09
Eu entendo que eles são ruins. Estou fazendo computação distribuída e preciso compartilhar um objeto entre várias tarefas. Em vez de inicializar deterministicamente uma variável estática, gostaria de abstrair a lógica em um Singleton. Eu imagino que eu poderia tornar o getInstance sincronizado. Isso funcionaria? O que eu preciso fazer é carregar um arquivo uma vez para muitas tarefas e somente após o envio da primeira tarefa. (Não quero que meus dados sejam serializados.) Pensei em tornar meu AbstractFileReader um argumento para o método getInstance, a fim de tornar o Singleton mais flexível. Eu valorizo ​​sua entrada.
Eu acho que você pode entender mal o que "distribuído" significa? Existem outras maneiras de conseguir o que você deseja: você considerou a injeção de dependência? Ou JNDI?
23610 oxbow_lakes