【问题标题】:How to instantiate a specific implementation, based on a certain class?如何基于某个类实例化特定的实现?
【发布时间】:2012-08-24 07:21:21
【问题描述】:

我有几个扩展 bean 的对象:

public class Fruit {}

public class Banana extends Fruit {}

public class Pear extends Fruit {}

我有一个不同实现的接口,每个 bean 一个。

public interface Milkshake {
    public String doMilkshake();
}

public class FruitMilkshake implements Milkshake {
    public String doMilkshake() {
        return "Fruit Milkshake!";
    }
}

public class BananaMilkshake implements Milkshake {
    public String doMilkshake() {
        return "Banana Milkshake!";
    }
}

public class PearMilkshake implements Milkshake {
    public String doMilkshake() {
        return "Pear Milkshake!";
    }
}

如何根据 bean 的具体类型实例化正确的实现?

现在我使用了类型化和 Map 来“映射”正确的实现。 像这样:

public void hungry(Fruit fruit) {
    Map<String, String> obj2impl = new HashMap<String, String>();
    obj2impl.put("Fruit", "FruitMilkshake");
    obj2impl.put("Banana", "BananaMilkshake");
    obj2impl.put("Pear", "PearMilkshake");

    String name = fruit.getClass().getCanonicalName();
    String implName = obj2impl.get(name);
    Milkshake milkshake = (Milkshake) Class.forName(implName).newInstance();

    milkshake.doMilkshake(fruit);
}



public interface Milkshake <T t> {
    public String doMilkshake(T t);
}

public class FruitMilkshake implements Milkshake<Fruit> {
    public String doMilkshake(Fruit fruit) {
        return "Fruit Milkshake!";
    }
}

public class BananaMilkshake implements Milkshake<Banana> {
    public String doMilkshake(Banana banana) {
        return "Banana Milkshake!";
    }
}

public class PearMilkshake implements Milkshake<Pear> {
    public String doMilkshake(Pear pear) {
        return "Pear Milkshake!";
    }
}

实现这一目标的更好方法?

【问题讨论】:

  • 您不应在每次调用 hungry 时重新创建水果图。而是将该地图作为实例变量。或者打开水果名称。

标签: java reflection interface


【解决方案1】:

实现这一目标的更好方法?

一个起点是避免反映类的名称:

Map<String, Class<? extends Milkshake>> obj2impl =
    new HashMap<String, Class<? extends Milkshake>>();
obj2impl.put("Fruit", FruitMilkshake.class);
obj2impl.put("Banana", BananaMilkshake.class);

...

Milkshake milkshake = obj2impl.get(text).newInstance();

现在这仍然要求您在每个实现中都有一个无参数构造函数,并且它仍然总是创建一个新实例。如果你使用类似Provider 的概念,你可以绕过这个:

Map<String, Provider<Milkshake>> map = ...;
// Fill the map with providers, some of which could create a new instance,
// and some could reuse an existing one

...

Milkshake milkshake = map.get(text).get();

编辑:重读帖子后,您也可以去掉文本部分,并拥有Map&lt;Class&lt;?&gt;, Provider&lt;Milkshake&gt;&gt;。尽可能避免对类的名称进行硬编码。

当然,如果你的Fruit 类有一个makeMilkshake 抽象方法,那就更好了...

【讨论】:

  • 我显然没有使用这些名称。这只是一个例子。
  • @Enrichman:我并不是说这些是真实姓名,但是当您可以使用班级时,您正在使用地图中班级名称的文本而是字面意思。这就是我的观点。
  • 嗯,好的,知道了。你是对的。 :) 顺便说一句,我在我的项目中使用 Guice,所以提供者的东西会很有用。 IoC 是我认为的唯一方式。
【解决方案2】:
public class Fruit {
    public Class<? extends Milkshake> getMilkshakeClass() {
        return Milkshake.class;
    }
}

public class Banana extends Fruit {
    public Class<? extends Milkshake> getMilkshakeClass() {
        return Banana.class;
    }
}

public class Pear extends Fruit {
    public Class<? extends Milkshake> getMilkshakeClass() {
        return PearMilkshake.class;
    }
}

您甚至可能想要创建一个 AbstractFruit 类,其中包含上面使用的方法作为它们必须覆盖的抽象方法

public abstract class AbstractFruit {
    public abstract Class<? extends Milkshake> getMilkshakeClass();
}

与其让它们扩展 Fruit,不如扩展它。

【讨论】:

  • 现在您正在绑定Fruit, Banana and Pear 以了解Milkshake。为什么他们会对奶昔有任何了解?
  • 因为你可以拿一个水果来做一杯奶昔?这是一个完全合理的想法。他没有说明他们对彼此的确切了解,但他们都是公开的,所以我假设他们都互相认识
  • 如果他打算有很多这样的不同选择,他怎么知道要做什么?对于给定的每种水果,该项目必须只有一个选项才能生产。否则饥饿的方法不知道该使用什么。因此,如果每个水果都有一个相应的项目要制作,这很好
猜你喜欢
  • 2017-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多