确保类的唯一实例的方法?


14

我正在寻找不同的方法来确保给定类的每个实例都是唯一可识别的实例。

例如,我有一个Name带field 的课name。一旦我有一个初始化为John Smith 的Name对象,name我就不想再实例化一个Name名称为John Smith的对象,或者如果实例化发生了,我希望将对原始对象的引用传递回去比一个新的对象。

我知道这样做的一种方法是让一个静态工厂保存Map所有当前的Name对象,并且该工厂在将引用返回给a之前检查是否存在以John Smith为名称的对象。Name宾语。

我可以想到的另一种方法是在Name类中具有一个静态Map,并且当传入的for name值已经在另一个对象中使用时,在调用构造函数时引发异常,但是我知道抛出异常在构造函数中通常是个坏主意

还有其他方法可以做到这一点吗?


1
您想要单身人士

5
您最好选择第一个:-静态工厂
Rohit Jain 2012年

16
@MarcB op不需要单例。他可能有许多相同类别的实例,但是这些实例必须是可识别的。

1
@MarcB我知道单例模式,但是我认为可以确保只允许一个类的实例吗?我想要多个实例,不同的值。很抱歉,如果这个问题不清楚。编辑:发布前仅看到第一条评论。
花生

2
I'm aware that one way of doing this is to have a static factory that holds a Map...那么,为什么不想要这样呢?
FrustratedWithFormsDesigner 2012年

Answers:


13

实际上,您已经回答了您的问题。您的第一种方法应该在这里更有效。使用static factory总是比理想constructor,无论你认为你可以。因此,您可以避免Constructor在这种情况下使用,否则,throw some exception如果已经存在具有给定名称的实例,则可以避免使用。

因此,您可以创建一个静态工厂方法:- getInstanceWithName(name)该方法将获取具有该名称的现有实例,如果该实例不存在,则将创建一个新实例并设置您的constructor私有实例,因为大多数情况下,在处理时static factories

此外,为此,您需要在类中维护静态ListMap所有创建的唯一实例Factory

编辑:-

您肯定应该经历- 有效的Java项目#1:考虑构造函数之上的静态工厂。没有比这本书更好的解释了。


1
OP已经想到了这一点。
BGurung 2012年

+1链接,这似乎清楚地解决了OP的一些问题。
罗伯特·哈维

@RobertHarvey。是的,这本书是这类主题中最好的资料。这实际上是第一项。:)
Rohit Jain 2012年

+1是有效Java的书,实际上我现在坐在书包里:)但是,我只是对其他实现唯一性的方法感到好奇,除了静态工厂/静态方法和构造函数中的异常。如果没有,我会接受的。
花生2012年

@花生。当然。那是挖掘以获得更多信息的最佳资源。
罗希特·贾因

5

有效Java的提法似乎增加了很多公信力,所以此答案可参考:

  • 有效的Java项目8:在重载等于时遵守总协定
  • 有效的Java项目9:重写等于时始终重写hashCode
  • 有效的Java项目15:最小化可变性

我将退后一步,并问您为什么要关心这个名称对象是否有多个实例。

我很少需要进行这种对象池化。我猜测OP正在这样做,因此他们可以简单地将Name对象与进行比较==。或使用或类似键Name内的对象HashMap

如果是这样,可以通过适当地实现equals()来解决。

像这样:

public final class Name {
  private final String name;

  public Name(String name) {
    if (name == null) {
      name = ""; //or exception if you like
    }
    this.name = name;
  }

  public String getName() {
    return name;
  }

  @Override
  public boolean equals(Object o) {
    if (!(o instanceof Name)) {
      return false;
    }
    Name other = (Name) o;
    return other.name.equals(name);
  }

  @Override
  public int hashCode() {
    return name.hashCode();
  }
}

完成后,将满足以下条件:

Name a = new Name("weston");
Name b = new Name("weston");
assert(a.equals(b)); //same but:
assert(a!=b); //not the same instance
//test out the Name instances in a hashmap:
HashMap<Name,Object> map = new HashMap<Name,Object>();
Object detailsIn = new Object();
map.put(a,detailsIn);
Object detailsOut = map.get(b);
assert(detailsIn==detailsOut); //the map returned the same details object
//even though we put with `a` and got with `b` thanks to our correct equals implementation

我猜你的目标,但这样一来,你可以使用Name类在哈希映射等,并且它们并不必须是完全相同的实例。


请举个例子。
罗伯特·哈维

@RobertHarvey正确执行的示例?
weston 2012年

看起来您提供了一个。但是我不清楚这与仅在静态工厂方法中检查Name属性是否相等有何不同。
罗伯特·哈维

1
@RobertHarvey我试图解释更多,我提供了一个完全不需要静态工厂的完整替代方案,并且我挑战OP的起点,即他们不希望每个名称具有多个实例。
weston,2012年

要求唯一对象的有效理由是,是否希望同步块将对象用作监视器。彼此相等的两个对象将不起作用。
利·考德威尔

3
  • 制作Name界面
  • NameFactory使用方法创建接口Name getByName(String)
  • NameFactoryMap<String,WeakReference<Name>>内部创建一个实现
  • synchronizegetByName创建新实例之前,在方法内按名称在地图上Name
  • (可选)Name在您的实现的内部使用接口的静态私有实现NameFactory

这种方法将使您确保:

  • Name任何时候都只有一个实例,
  • 当班级停留的Name时间超过他们的需要时,您的班级就不会出现“ Lingerer”内存泄漏,
  • 由于使用接口,因此该设计仍可通过模拟对象进行测试。

如果throw存在相同的名称而不返回对象,或者返回对象,则使用工厂方法也不会发生内存泄漏null
罗伯特·哈维

@RobertHarvey我的意思是,这些泄漏并不是真正的泄漏,而是合法的对象(所谓的“徘徊者”)。一个简单的静态映射将防止释放其值对象,而弱引用的映射将仅在存在其他活动引用的情况下将其保存在内存中。
dasblinkenlight 2012年

啊,我明白你的意思了。这可能是a destructor派上用场的罕见情况之一。
罗伯特·哈维

1
太复杂了。可以像@weston答案一样进行操作,覆盖等于并让工厂保留名称的集合。
图兰斯·科尔多瓦

@ user1598390应该使事情尽可能简单,但不要简单。weston的“解决方案”不能解决OP的问题(没有映射),并且名称的集合会造成持久的内存泄漏(是的,Java中存在内存泄漏)。
dasblinkenlight 2012年

1

您应该将构造函数getNameInstance(String)设为私有并创建类似的方法,如果已经存在相同名称的对象(例如,基于静态类的hastable),则返回该引用,否则使用私有构造函数创建一个新对象并将其添加到哈希表


您已经重复了我在问题第三段中所说的内容。我一直在寻找其他方式。
花生2012年

抱歉,我想我们几乎同时回答,因为我在发布我的信息之前检查了答案。实际上,我尝试在StackOverflown上回答它,然后迁移了它。
HericDenis 2012年

1

尝试以下。您必须跟踪创建的每个对象。为此,我使用列表。并将类构造函数设为私有,以便可以在创建实例之前应用预检查

class UniqueName
    {
        private string Name;
        public int ID;
        private static int Count=0;
        static List<UniqueName> lt=new List<UniqueName>();

        private UniqueName(string Name)
        {
            this.Name = Name;
            ID = ++Count;
        }

        public static UniqueName GetUniqueueInstance(string Name)
        {
            foreach (UniqueName un in lt)
            {
                if ( string.Compare( un.Name,Name,StringComparison.InvariantCultureIgnoreCase)==0)
                    return un;
            }

            UniqueName temp=new UniqueName(Name);
            lt.Add(temp);
            return temp;
        }
    }

这不是Java吗?你写过伪代码了吗?或者,用其他任何语言?请明确指定。
罗希特·贾因

看起来像C#。Akshay,除了之外,您还应该学习其他收藏List。这将是一个很好的选择Dictionary<string,UniqueName>
weston 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.