Java 中带参数的单例

发布于 2024-07-25 18:33:02 字数 2399 浏览 4 评论 0原文

我正在阅读 Wikipedia 上的 Singleton 文章,并且遇到了这个示例:

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;
    }
}

虽然我真的很喜欢这个 Singleton 的行为方式,但我不知道如何调整它以将参数合并到构造函数中。 在 Java 中执行此操作的首选方法是什么? 我必须做这样的事情吗?

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;
    }
}

谢谢!


编辑:我想我因想要使用 Singleton 而引发了一场争议风暴。 让我解释一下我的动机,希望有人能提出更好的想法。 我正在使用网格计算框架来并行执行任务。 一般来说,我有这样的情况:

// 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).
    }
}

发生的情况是,即使我只是将数据的引用传递给所有任务,当任务被序列化时,数据也会被一遍又一遍地复制。 我想做的是在所有任务之间共享该对象。 当然,我可能会像这样修改类:

// 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).
    }
}

正如您所看到的,即使在这里我也遇到了这样的问题:传递不同的文件路径在传递第一个文件路径后没有任何意义。 这就是为什么我喜欢答案中发布的商店的想法。 无论如何,我不想将加载文件的逻辑包含在 run 方法中,而是想将此逻辑抽象为 Singleton 类。 我不会提供另一个例子,但我希望你能明白。 请让我听听您的想法,以更优雅的方式来完成我想做的事情。 再次感谢你!

I was reading the Singleton article on Wikipedia and I came across this example:

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;
    }
}

While I really like the way this Singleton behaves, I can't see how to adapt it to incorporate arguments to the constructor. What is the preferred way to do this in Java? Would I have to do something like this?

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;
    }
}

Thanks!


Edit: I think I have started a storm of controversy with my desire to use Singleton. Let me explain my motivation and hopefully someone can suggest a better idea. I am using a grid computing framework to execute tasks in parallel. In general, I have something like this:

// 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).
    }
}

What happens is that even though I merely pass a reference to my data to all of the tasks, when the tasks are serialized, the data gets copied over and over. What I want to do is share the object among all of the tasks. Naturally, I might modify the class like so:

// 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).
    }
}

As you can see, even here I have the issue that passing a different file path means nothing after the first one is passed. This is why I like the idea for a store which was posted in the answers. Anyhow, rather than including the logic for loading the file in the run method, I wanted to abstract this logic into a Singleton class. I will not provide yet another example, but I hope you get the idea. Please let me hear your ideas for a more elegant way to accomplish what I am trying to do. Thank you again!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(21

凉宸 2024-08-01 18:33:03

我会非常清楚地阐明我的观点:带参数的单例不是单例

根据定义,单例是您只想实例化一次的对象。 如果您尝试向构造函数提供参数,那么单例的意义何在?

你有两个选择。 如果您希望使用一些数据来初始化您的单例,您可以在实例化后加载数据,如下所示:

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

如果您的单例正在重复执行操作,并且每次都使用不同的参数,您可能会以及将参数传递给正在执行的主方法:

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

在任何情况下,实例化始终是无参数的。 否则你的单身人士就不是单身人士。

I'll make my point very clear: a singleton with parameters is not a singleton.

A singleton, by definition, is an object you want to be instantiated no more than once. If you are trying to feed parameters to the constructor, what is the point of the singleton?

You have two options. If you want your singleton to be initialized with some data, you may load it with data after instantiation, like so:

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

If the operation your singleton is performing is recurring, and with different parameters every time, you might as well pass the parameters to the main method being executed:

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

In any case, instantiation will always be parameter-less. Otherwise your singleton is not a singleton.

油焖大侠 2024-08-01 18:33:03

我认为你需要像工厂这样的东西来实例化和重用具有各种参数的对象。 它可以通过使用同步 HashMapConcurrentHashMap 将参数(例如 Integer)映射到“单例”参数化类来实现。

尽管您可能会遇到应该使用常规的非单例类的情况(例如需要 10.000 个不同参数化的单例)。

下面是此类存储的一个示例:

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;
        }
    }
}

为了进一步推动它,Java 枚举也可以被视为(或用作)参数化单例,尽管只允许固定数量的静态变体。

但是,如果您需要分布式1解决方案,请考虑一些横向缓存解决方案。 例如:EHCache、Terracotta 等。

1 是指跨越可能多台计算机上的多个虚拟机。

I think you need something like a factory to have objects with various parameters instantiated and reused. It could be implemented by using a synchronized HashMap or ConcurrentHashMap map a parameter (an Integer for an example) to your 'singleton' parameterizable class.

Although you might get to the point where you should use regular, non-singleton classes instead (for example needing 10.000 differently parametrized singleton).

Here is an example for such store:

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;
        }
    }
}

To push it even further, the Java enums can be also considered (or used as) parametrized singletons, although allowing only a fixed number static variants.

However, if you need a distributed1 solution, consider some lateral caching solution. For example: EHCache, Terracotta, etc.

1 in the sense of spanning multiple VMs on probably multiple computers.

凯凯我们等你回来 2024-08-01 18:33:03

您可以添加可配置的初始化方法,以便将实例化与获取分开。

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;
    }

}

然后您可以调用 Singleton.init(123) 一次来配置它,例如在您的应用启动时。

You can add a configurable initialization method in order to separate instantiation from getting.

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;
    }

}

Then you can call Singleton.init(123) once to configure it, for example in your app startup.

不即不离 2024-08-01 18:33:03

如果您想表明某些参数是必需的,您也可以使用构建器模式。

    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);
        }

    }


}

然后您可以按如下方式创建/实例化/参数化它:

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

You can also use the Builder pattern if you want to show that some parameters are mandatory.

    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);
        }

    }


}

Then you could create/instantiate/parametrized it as follow:

public static void main(String[] args) {
    new EnumSingleton.SingletonBuilder("nico").age(41).build();
    EnumSingleton.getSingleton().print();
}
才能让你更想念 2024-08-01 18:33:03

令人惊讶的是没有人提到记录器是如何创建/检索的。 例如,下面显示了如何 Log4J 记录器 已检索。

// 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)

有一些间接级别,但关键部分如下 方法 几乎告诉了有关其工作原理的所有信息。 它使用哈希表来存储现有的记录器,并且密钥从名称派生。 如果给定名称的记录器不存在,它将使用工厂来创建记录器,然后将其添加到哈希表中。

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      } 
...

Surprised that no one mentioned how a logger is created/retrieved. For example, below shows how Log4J logger is retrieved.

// 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)

There are some levels of indirections, but the key part is below method which pretty much tells everything about how it works. It uses a hash table to store the exiting loggers and the key is derived from name. If the logger doesn't exist for a give name, it uses a factory to create the logger and then adds it to the hash table.

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      } 
...
酒浓于脸红 2024-08-01 18:33:03

带参数的单例不是单例”的说法不完全正确。 我们需要从应用程序的角度而不是从代码的角度来分析这一点。

我们构建单例类来在一个应用程序运行中创建对象的单个实例。 通过使用带参数的构造函数,您可以在代码中构建灵活性,以便在每次运行应用程序时更改单例对象的某些属性。 这并不违反单例模式。 如果您从代码的角度来看这似乎是一种违规。

设计模式是为了帮助我们编写灵活且可扩展的代码,而不是阻碍我们编写优秀的代码。

"A singleton with parameters is not a singleton" statement is not completely correct. We need to analyze this from the application perspective rather than from the code perspective.

We build singleton class to create a single instance of an object in one application run. By having a constructor with parameter, you can build flexibility into your code to change some attributes of your singleton object every time you run you application. This is not a violation of Singleton pattern. It looks like a violation if you see this from code perspective.

Design Patterns are there to help us write flexible and extendable code, not to hinder us writing good code.

别把无礼当个性 2024-08-01 18:33:03

使用 getter 和 setter 设置变量并将默认构造函数设为私有。 然后使用:

Singleton.getInstance().setX(value);

Use getters and setters to set the variable and make the default constructor private. Then use:

Singleton.getInstance().setX(value);
何其悲哀 2024-08-01 18:33:03

使用 Bill Pugh 的按需初始化惯用法 修改单例模式。 这是线程安全的,没有专门的语言构造的开销(即易失性或同步):

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);
    }
}

Modification of Singleton pattern that uses Bill Pugh's initialization on demand holder idiom. This is thread safe without the overhead of specialized language constructs (i.e. volatile or synchronized):

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);
    }
}
固执像三岁 2024-08-01 18:33:03

如果你想创建一个作为 Context 的 Singleton 类,一个好的方法是拥有一个配置文件并从 instance() 内部的文件中读取参数。

如果在程序运行期间动态获取提供给 Singleton 类的参数,只需使用静态 HashMap 在 Singleton 类中存储不同的实例即可确保对于每个参数仅创建一个实例。

If you want to create a Singleton class serving as a Context, a good way is to have a configuration file and read the parameters from the file inside instance().

If the parameters feeding the Singleton class are got dynamically during the running of your program, simply use a static HashMap storing different instances in your Singleton class to ensure that for each parameter(s), only one instance is created.

秋意浓 2024-08-01 18:33:03

你无法理解如何完成你想要做的事情的原因可能是你想要做的事情实际上没有意义。 您想使用不同的参数调用 getInstance(x),但始终返回相同的对象? 当您调用 getInstance(2) 然后调用 getInstance(5) 时,您想要什么行为?

如果你想要同一个对象,但它的内部值不同,这是它仍然是单例的唯一方法,那么你根本不需要关心构造函数; 您只需在对象的出口处设置 getInstance() 中的值即可。 当然,您知道对单例的所有其他引用现在都具有不同的内部值。

另一方面,如果您希望 getInstance(2)getInstance(5) 返回不同的对象,则您没有使用 Singleton 模式,而是使用工厂模式。

The reason you can't make sense of how to accomplish what you're trying to do is probably that what you're trying to do doesn't really make sense. You want to call getInstance(x) with different arguments, but always return the same object? What behavior is it you want when you call getInstance(2) and then getInstance(5)?

If you want the same object but for its internal value to be different, which is the only way it's still a singleton, then you don't need to care about the constructor at all; you just set the value in getInstance() on the object's way out. Of course, you understand that all your other references to the singleton now have a different internal value.

If you want getInstance(2) and getInstance(5) to return different objects, on the other hand, you're not using the Singleton pattern, you're using the Factory pattern.

穿越时光隧道 2024-08-01 18:33:03

在您的示例中,您没有使用单例。 请注意,如果执行以下操作(假设 Singleton.getInstance 实际上是静态的):

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

则 obj2.x 的值是 3,而不是 4。如果需要执行此操作,请将其设为普通类。 如果值的数量较小且固定,您可以考虑使用枚举。 如果您遇到过多的对象生成问题(通常不是这种情况),那么您可以考虑缓存值(并检查源或获取帮助,因为很明显如何构建缓存而不存在内存泄漏的危险)。

您可能还想阅读这篇文章,因为单例可以非常容易过度使用。

In your example you are not using a singleton. Notice that if you do the following (assuming that the Singleton.getInstance was actually static):

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

Then the obj2.x's values is 3, not 4. If you need to do this, make it a plain class. If the number of values is small and fixed, you can consider using an enum. If you are having problem with excessive object generation (which is usually not the case), then you can consider caching values (and check sources or get help with that, as it is obvious how to build caches without the danger of memory leaks).

You also might want to read this article as singletons can be very easily overused.

情愿 2024-08-01 18:33:03

单例是反模式的另一个原因是,如果根据建议使用私有构造函数编写,则它们很难子类化并配置为在某些单元测试中使用。 例如,维护遗留代码时需要。

Another reason Singletons are an anti-pattern is that if written according to recommendations, with private constructor, they are very hard to subclass and configure to use in certain unit tests. Would be required in maintaining legacy code, for example.

我的鱼塘能养鲲 2024-08-01 18:33:03

我们不能做这样的事情吗:

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;
    }
}

Couldn't we do something like this:

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;
    }
}
诗笺 2024-08-01 18:33:03

这不完全是单例,但可能可以解决您的问题。

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;
  }
}

This is not quite a singleton, but may be something that could fix your problem.

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;
  }
}
情释 2024-08-01 18:33:03

我害怕将此作为答案发布,但我不明白为什么没有人考虑这个,也许这个答案也已经给出了,我只是不明白。

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");
    }
  }
}

由于 getInstance() 每次都会返回相同的实例,我认为这可以工作。
如果这是错误的,我会删除它,我只是对这个主题感兴趣。

I'm scared to post this as an answer, but I don't understand why nobody think about this, maybe this answer was also given already I just didn't understand it.

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");
    }
  }
}

Since the getInstance() returns the same Instance everytime, I think this could work.
If this is wrong to much I will delete it, I'm just interested in this topic.

望笑 2024-08-01 18:33:03

如果我们将问题视为“如何使用状态创建单例”,那么没有必要将状态作为构造函数参数传递。 我同意在获取单例实例后初始化状态或使用 set 方法的帖子。

另一个问题是:有状态的单例好吗?

If we take the problem as "how to make singleton with state", then it is not necessary to pass the state as constructor parameter. I agree with the posts that initialize the states or using set method after getting the singleton instance.

Another question is: is it good to have singleton with state?

二货你真萌 2024-08-01 18:33:03

尽管有些人可能会断言,这里是一个在构造函数中带有参数的单例

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
     */
}

单例模式说的是:

  • 确保单例类的只有一个实例存在,
  • 提供对该实例的全局访问。

这个例子就尊重了这些。

为什么不直接设置属性呢? 这是教科书案例,展示了我们如何获得具有带参数的构造函数的单例,但它在某些情况下可能很有用。 例如,在继承情况下强制单例设置一些超类属性。

In spite of what some may assert, here is a singleton with parameters in constructor

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
     */
}

The singleton pattern say :

  • ensure that only one instance of the singleton class ever exists
  • provide global access to that instance.

which are respected with this example.

Why not directly set the property ? It's textbook case to show how we can get a singleton having constructor with parameter but it could be useful in some situations. For example in inheritance cases to force the singleton to set some superclass properties.

小梨窩很甜 2024-08-01 18:33:03

需要添加一些内容,如果希望参数只应初始化一次并且不得被覆盖,那么只需实施检查并在有人尝试再次初始化它们时抛出异常。 前任。:

public class Service {

private String host = null;
private String port = null;

private Service() {
}

private static class ServiceSingletonHolder {

    private static final Service INSTANCE = new Service();
}

public static Service getInstance() {
    return ServiceSingletonHolder.INSTANCE;
}

public void initilize(String host, String port) {
    if (this.host != null && host != null) {
        throw new IllegalArgumentException("host can not be overwritten");
    }

    if (this.port != null && port != null) {
        throw new IllegalArgumentException("port can not be overwritten");
    }

    this.host = host;
    this.port = port;
}
}

Something to add, in case if want that the parameters should only be initialized once and must not be overwritten then just implement a check and throw exception if somebody tries to initialized them again. Ex.:

public class Service {

private String host = null;
private String port = null;

private Service() {
}

private static class ServiceSingletonHolder {

    private static final Service INSTANCE = new Service();
}

public static Service getInstance() {
    return ServiceSingletonHolder.INSTANCE;
}

public void initilize(String host, String port) {
    if (this.host != null && host != null) {
        throw new IllegalArgumentException("host can not be overwritten");
    }

    if (this.port != null && port != null) {
        throw new IllegalArgumentException("port can not be overwritten");
    }

    this.host = host;
    this.port = port;
}
}
眼眸印温柔 2024-08-01 18:33:03

我认为这是一个普遍的问题。 将单例的“初始化”与单例的“获取”分开可能会起作用(此示例使用双重检查锁定的变体)。

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;
    }

    ...
}

I think this is a common problem. Separating the "initialization" of the singleton from the "get" of the singleton might work (this example uses a variation of double checked locking).

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;
    }

    ...
}
千纸鹤 2024-08-01 18:33:03

单例通常被认为是反模式,不应该使用。 它们不会使代码易于测试。

无论如何,带有参数的单例是没有意义的 - 如果您写道:

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

您的单例也不是线程安全的,因为多个线程可以同时调用getInstance,从而导致创建多个实例(可能具有不同的 x 值)。

Singletons are generally considered to be anti-patterns and shouldn't be used. They do not make code easy to test.

A singleton with an argument makes no sense anyway - what would happen if you wrote:

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

Your singleton is also not thread-safe as multiple threads can make simultaneous calls to getInstance resulting in more than one instance being created (possibly with different values of x).

一腔孤↑勇 2024-08-01 18:33:03

当然,单例是一种“反模式”(假设定义了具有可变状态的静态)。

如果您想要一组固定的不可变值对象,那么枚举就是正确的选择。 对于大型的、可能是开放式的值集,您可以使用某种形式的存储库 - 通常基于 Map 实现。 当然,当您处理静态时,请小心线程(要么足够广泛地同步,要么使用 ConcurrentMap 检查另一个线程没有击败您,或者使用某种形式的 future)。

Singleton is, of course, an "anti-pattern" (assuming a definition of a static with variable state).

If you want a fixed set of immutable value objects, then enums are the way to go. For a large, possibly open-ended set of values, you can use a Repository of some form - usually based on a Map implementation. Of course, when you are dealing with statics be careful with threading (either synchronise sufficiently widely or use a ConcurrentMap either checking that another thread hasn't beaten you or use some form of futures).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文