Java 同一配置的多个构造函数应只创建一个实例

Java 同一配置的多个构造函数应只创建一个实例,java,design-patterns,Java,Design Patterns,我定义了几个类: 食物 面包 黄油 食品厂 详情如下: Food.java public abstract class Food { private String id; public String getId() { return id; } public void setId(String id) { this.id = id; } public abstract void eat(); publ

我定义了几个类: 食物 面包 黄油 食品厂

详情如下:

Food.java

public abstract class Food {
    private String id;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public abstract void eat();

    public Food(String id) {
        this.id = id;

        FoodFactory.getInstance().foodWasConstructed(this);
    }
}
<pre><code>public class SampleTest {
    public static void main(String[] args) {
        Food food1 = new Bread("1");
        Food food2 = new Bread("1");

        System.out.println(food1.hashCode());

        System.out.println(food2.hashCode());
        System.out.println(food1.equals(food2));

        // food2 should return food1 reference. How can I accomplish this.
        // food2 creates entirely new instance. What I want is it should refer
        // to food1 instance.
        // How can i do this. Is there any design flaw?
        FoodFactory.getInstance().getFood("1", "bread").eat();
    }
}</pre></code>
FoodFactory.java

public class FoodFactory {
    private Map<String, Food> map = new HashMap<String, Food>();
    private static FoodFactory factory = new FoodFactory();

    private FoodFactory() {
    }

    public static FoodFactory getInstance() {
        return factory;
    }

    public synchronized Food getFood(String id, String type) {
        if(map.get(id) != null) {
            return map.get(id);
        } else {
            if(type.equals("bread")) {
                Food food = new Bread(id);
                map.put(id, food);
                return food;
            } else if(type.equals("butter")) {
                Food food = new Butter(id);
                map.put(id, food);
                return food;
            }
        }
        return null;
    }

    public void foodWasConstructed(Food food) {
        if(!map.containsKey(food.getId()))
            this.map.put(food.getId(), food);
    }
}
Butter.java

public class Butter extends Food {

    public Butter(String id) {
        super(id);
    }

    public void eat() {
        System.out.println("Eating butter");
    }

}
现在我的要求是不能创建两个具有相同id的食物实例。 我尝试运行以下代码:

public class SampleTest {
    public static void main(String[] args) {
        Food food1 = new Bread("1");
        Food food2 = new Bread("1");

        // food2 should return food1 reference. How can I accomplish this.
        // food2 creates entirely new instance. What I want is it should refer to food1 instance.
        // How can i do this. Is there any design flaw?
        FoodFactory.getInstance().getFood("1", "bread").eat();
    }
}
并获得以下输出:

Eating bread : 2
我的问题如评论中所述: food2应返回food1引用。我怎样才能做到这一点。
food2创建了一个全新的实例。我想要的是它应该引用food1实例。我怎样才能做到这一点。是否存在任何设计缺陷?

您需要基于变量
id
Food
类实现
hashCode()
equals()
方法。因此,对于传递给构造函数的每个类似的
id
,只会创建一个类。当
Bread
类扩展
Food
时,它会获得相同的行为,并且只为相同的
id
创建一个类

您可以将您的
食品
课程更改为以下

 public abstract class Food {
    private String id;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public abstract void eat();

    public Food(String id) {
        this.id = id;

        FoodFactory.getInstance().foodWasConstructed(this);
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((id == null) ? 0 : id.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Food other = (Food) obj;
        if (id == null) {
            if (other.id != null)
                return false;
        } else if (!id.equals(other.id))
            return false;
        return true;
    }

}

SampleTest.java


您需要基于变量
id
Food
类实现
hashCode()
equals()
方法。因此,对于传递给构造函数的每个类似的
id
,只会创建一个类。当
Bread
类扩展
Food
时,它会获得相同的行为,并且只为相同的
id
创建一个类

您可以将您的
食品
课程更改为以下

 public abstract class Food {
    private String id;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public abstract void eat();

    public Food(String id) {
        this.id = id;

        FoodFactory.getInstance().foodWasConstructed(this);
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((id == null) ? 0 : id.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Food other = (Food) obj;
        if (id == null) {
            if (other.id != null)
                return false;
        } else if (!id.equals(other.id))
            return false;
        return true;
    }

}

SampleTest.java

尝试像这样更改FoodFactory()构造函数

公共类食品工厂{
私人地图;
私营静态食品厂;
私人食品厂{
}
公共静态FoodFactory getInstance(){
如果(工厂==null)
{
工厂=新食品工厂();
map=新的HashMap();
}
返回工厂;
}
}
它将始终返回相同的FoodFactory实例引用。

尝试像这样更改FoodFactory()构造函数

公共类食品工厂{
私人地图;
私营静态食品厂;
私人食品厂{
}
公共静态FoodFactory getInstance(){
如果(工厂==null)
{
工厂=新食品工厂();
map=新的HashMap();
}
返回工厂;
}
}


它将始终返回相同的FoodFactory实例引用。

您不能使用
new
操作符执行此操作,这就是您使用静态工厂的目的,就像您在
FoodFactory
中已经使用的那样。检查键是否已存在于映射中如何?这在我看来是合理的。您还需要使用静态工厂来获取食品的新实例。@FabienThouraud据我所知,它已经在
FoodFactory
中完成了。@biziclop很好。。。我想我太累了,知道吗。。。很抱歉误读。@user3057107,你能试一下我的答案吗?你不能用
new
操作符,这就是你使用静态工厂的目的,就像你在
FoodFactory
中已经做过的那样。检查这个键是否已经存在于地图中呢?这在我看来是合理的。您还需要使用静态工厂来获取食品的新实例。@FabienThouraud据我所知,它已经在
FoodFactory
中完成了。@biziclop很好。。。我想我太累了,知道吗。。。很抱歉误读。@user3057107,你能试一下我的答案吗?问题是关于
食品
而不是
食品工厂
。而OPs代码现在只会返回对
FoodFactory
的相同引用。同意@Codebender:我希望以food2引用food1实例的方式对代码进行一些更改。@user3057107将类设置为singletono。。。。。可以有两个或多个具有不同id的不同食物实例,例如id=1、id=2等,但只有一个实例具有相同的id,并且也使用食物构造函数。我的程序可能存在一些设计问题。为什么要打印
i
,您应该打印
id
。如果您根据我的回答更改
FoodFactory
类,那么map中只有一个实例具有相同的
id
。`public void eat(){System.out.println(“Eating bread:+getId();}`问题是关于
食品的
而不是
FoodFactory
。而OPs代码现在只会返回对
FoodFactory
的相同引用。同意@Codebender:我希望以food2引用food1实例的方式对代码进行一些更改。@user3057107将类设置为singletono。。。。。可以有两个或多个具有不同id的不同食物实例,例如id=1、id=2等,但只有一个实例具有相同的id,并且也使用食物构造函数。我的程序可能存在一些设计问题。为什么要打印
i
,您应该打印
id
。如果您根据我的回答更改
FoodFactory
类,map中只有一个实例具有相同的
id
。`public void eat(){System.out.println(“Eating bread:+getId());}`我想您没有理解我的问题。。。。。换句话说,你可以说,我调用了两个具有相同参数的构造函数,但在memoryYes中只创建了一个对象,你尝试过我的实现吗?food2现在引用food1作为上述输出。这不是你想要的吗?都德,只要粘贴上面的食物类代码,执行你想要的,检查你是否得到了想要的输出。吃面包:2应该是吃面包:1。请仔细阅读问题。equals方法只是比较两个不同的实例。两人的记忆都很丰富。我希望food1和food2引用相同的堆位置。现在你明白我的意思了吗?你怎么能期望呢?Bread中的变量i是静态的,因此每次调用计数器时都会递增。当构造函数被调用两次时,i只能是2,但不能是1。当Food food1=新面包(“1”)时,i增加;在FoodF的getFood方法中再次执行
  80
    80
    true
    Eating bread : 2
public class FoodFactory {
    private Map<String, Food> map;
    private static FoodFactory factory;

    private FoodFactory() {
    }

    public static FoodFactory getInstance() {
if(factory==null)


{
factory = new FoodFactory();
map = new HashMap<String, Food>();

}
        return factory;
    }

}