【问题标题】:Write a java class to have only five instance of it created编写一个 java 类,只创建它的五个实例
【发布时间】:2010-08-03 17:49:13
【问题描述】:

我想编写一个只能实例化 5 次的 java 类,就像你的单例类只有一个实例一样。

除此之外,实例应该循环选择。

假设我有一个 A 类。我应该只能创建该类的 5 个实例。 假设我有 InstanceA_1、InstanceA_2、InstanceA_3、InstanceA_4、InstanceA_5。每当我需要使用它们时,都应该在循环的基础上挑选它们。

【问题讨论】:

标签: java design-patterns


【解决方案1】:

正如Effective Java 2nd Edition推荐enum来实现单例,这个解决方案也使用enum来实现...四元组?

import java.util.*;

public enum RoundRobin {
    EENIE, MEENIE, MINY, MO;

    private final static List<RoundRobin> values =
        Collections.unmodifiableList(Arrays.asList(values()));
    // cache it instead of creating new array every time with values()

    private final static int N = values.size();
    private static int counter = -1;

    public static RoundRobin nextInstance() {
        counter = (counter + 1) % N; // % is the remainder operator
        return values.get(counter);
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            System.out.println(RoundRobin.nextInstance());
        }
        // EENIE, MEENIE, MINY, MO, EENIE, MEENIE, MINY, MO, ...
    }
}

将此扩展到五元组是不言自明的。

另见

  • Effective Java 2nd Edition,使用私有构造函数或枚举类型强制单例属性

    从 1.5 版开始,还有第三种实现单例的方法。只需使用一个元素创建一个枚举类型。这种方法在功能上等同于公共字段方法,只是它更简洁,免费提供序列化机制,并且即使面对复杂的序列化或反射攻击,也可以防止多次实例化。虽然这种方法尚未被广泛采用,但单元素枚举类型是实现单例的最佳方式

相关问题

【讨论】:

  • @mchr:你猜错了。语言本身保证枚举具有严格控制的实例化,允许您使用 == 而不是 equals 如果您选择这样做。
  • 对不起,我应该更清楚我认为哪个部分是线程安全的 :)。 public static RoundRobin nextInstance() 此方法对 int 进行非同步访问。当然,多个线程最终可能会选择同一个实例。
  • @mchr:啊,是的,nextInstance 可以变成synchronizedcountervolatile,或者也许可以使用原子模块counter,等等。好点。
【解决方案2】:

您需要Object pool 之类的东西。最简单的实现是有一个包含 5 个对象的 List 的单例,并且每当您调用 getInstance(..) 时,应该迭代列表并返回其中一个对象。

【讨论】:

  • 我最初的反应是建议一个对象池,但 Kamal 没有提到需要签出/签入 A 类的五个实例,或者签出方法是否应该阻塞直到池化的 A 实例可用。
【解决方案3】:

这是作业吗?

你为什么不创建一个工厂来创建 5 个实例并将它们存储在一个列表中?当有人调用 Factory.getInstance() 时,它将使用列表的正确索引获取正确的实例。

【讨论】:

    【解决方案4】:

    游泳池可以工作吗(例如Apache Commons Pool)?

    【讨论】:

      【解决方案5】:

      嗯,大致是这样的:

      • 将构造函数设为私有
      • 在静态初始化器中创建五个实例,并将引用存储在数组中
      • 保留一个索引以指示下一个要返回的索引
      • 编写一个静态方法来返回下一个值并增加索引。

      为了线程安全,您需要在静态方法中锁定。您可以使用 AtomicInteger,但包装很棘手。您始终可以使用 AtomicIntegerAtomicLong,每次递增,并使用 % 运算符进行索引...但是当索引换行时,您会遇到不连续性。这对你来说可能是也可能不是问题。

      (这是假设您不介意预先创建所有五个实例。这肯定会让事情变得更容易。)

      【讨论】:

        【解决方案6】:

        我想您需要做的就是扩展单例模式以使用计数器。比如……

        // Declare a list holding the 5 Objects
        List<Foo> instances = new ArrayList<Foo>(5);
        // Keep track of which Object you are returning next
        int curr = 0;
        
        ...
        
        static Foo getInstance() {
            // Only instantiate when necessary
            if (instances.get(curr) == null) {
                instances.set(curr, new Foo());
            }
            Foo toReturn = instances.get(curr);
            // Use modulus to loop back to 0 after 4
            curr = (curr + 1) % 5;
            return toReturn;
        }
        

        免责声明:我假设您不需要代码是线程安全的。

        【讨论】:

        • 到达Integer.MAX_VALUE 时会发生什么?模数方法还能正确计算吗?
        • curr 永远不会大于 4。:)
        【解决方案7】:

        这是一个非常简单的解决方案。请注意,这不是线程安全的:

        class ClassA {
        
            private final List<ClassA> instances = Arrays.asList(new ClassA(), new ClassA(), new ClassA(), new ClassA(), new ClassA());
        
            private ClassA() {}
        
            public static ClassA getInstance() {
                Collections.shuffle(instances);
                return instances.get(0);
            }
        
        }
        

        【讨论】:

        • 随机播放不值得round robin
        • @BalusC 是的,但是它不会达到预期的结果吗?
        • 随机播放:可能返回 3、5、1、1、1、2、5、1、1、4 等。循环:返回 1、2、3、4、5、1, 2、3、4、5等
        • @BalusC - 感谢您的澄清。
        【解决方案8】:

        创建五个静态实例。保留最后一个实例的索引。创建一个私有构造函数。使用方法获取实例(类似于单例);

        public class MyObject {
        
        private static final MyObject instance_1 = new MyObject();
        private static final MyObject instance_2 = new MyObject();
        private static final MyObject instance_3 = new MyObject();
        private static final MyObject instance_4 = new MyObject();
        private static final MyObject instance_5 = new MyObject();
        
        private static final MyObject[] instances = new MyObject[]{instance_1, instance_2,instance_3, instance_4,instance_5};
        
        private MyObject() {
        
        }
        
        private static volatile int index = 0; //in a multi-threaded evironment, you should use volatile
        
            /**
             * The following code is not thread safe
             */
        public static MyObject getInstance() {
            MyObject instance = instances[index];
        
                    if (index == 4) {
                        index = 0;
                    } else {
                        index++;
                    }
            return instance;
        }
        

        }

        【讨论】:

        • Pssh:当返回最后一个元素时,您忘记将索引重置为零:)
        • 您为什么要费心保留五个单独的变量?为什么不直接循环初始化数组呢?
        • ...或在数组初始化期间:) 例如new MyObject[] { new MyObject(), ... }.
        • @Jon:我认为这样更具可读性。应该清楚,只有 5 个实例。但是如果有更多实例(> 20),我会使用 for 循环)。 :)
        • @THierry-Dimitry:而当初始化后没有使用的变量时我会怀疑......
        【解决方案9】:
         class OnlyFiveTime {
        
            private static LinkedList<OnlyFiveTime> instaces = new LinkedList<OnlyFiveTime>();
            private static final int count = 5;
        
            public static OnlyFiveTime getInstace() {
        
        
                if(instaces.size() != count) {
                    instaces.addLast(new OnlyFiveTime());
                }
        
                OnlyFiveTime robinRound = instaces.removeFirst();
        
                instaces.addLast(robinRound);
        
                return robinRound;
        
            }
        
            private OnlyFiveTime() {
        
            }
        
        }
        

        【讨论】:

          【解决方案10】:

          一个简化的循环。

          import java.util.*; 
          
          public enum RoundRobin { 
              EENIE, MEENIE, MINY, MO; 
          
              private final static RoundRobin[] values = values(); 
              private static int counter = -1; 
          
              public static RoundRobin nextInstance() {
                  counter = (counter + 1) % values.length;
                  return values[counter]; 
              } 
          
              public static void main(String[] args) { 
                  for (int i = 0; i < 10; i++) 
                      System.out.println(RoundRobin.nextInstance()); 
                  // EENIE, MEENIE, MINY, MO, EENIE, MEENIE, MINY, MO, ... 
              } 
          } 
          

          【讨论】:

            【解决方案11】:

            您必须创建静态实例变量来跟踪所有对象。然后您可以在构造函数上使用 if 语句来引发异常或在用户初始化类 5 次时退出系统。

            public class JustFive {
                public static int count;
                
                public JustFive(){
                    if(count>=5){
                        System.out.println("you can not create another instance");
                        System.exit(1);
                    }
                    else count++;
                    System.out.println(count);
            
                }}
            

            您可能想使用静态工厂方法。我正在向您展示不推荐的不寻常方式。

            【讨论】:

            • 虽然此代码可能/可能无法解决问题,including an explanation 关于如何以及为什么解决问题将真正有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提问的人。请编辑您的答案以添加解释。
            猜你喜欢
            • 2020-04-20
            • 1970-01-01
            • 2015-01-01
            • 2010-12-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-03-23
            • 1970-01-01
            相关资源
            最近更新 更多