Java中的多态工厂/ getInstance()

时间:2008-11-25 16:06:00

标签: java design-patterns polymorphism factory

我的目标是创建一组对象,每个对象都有一个唯一的标识符。如果已存在具有该标识符的对象,我想使用现有对象。否则我想创建一个新的。我试图不使用Singleton这个词,因为我知道这是一个肮脏的词......

我可以使用工厂方法:

    // A map of existing nodes, for getInstance.
private static Map<String, MyClass> directory = new HashMap<String, MyClass>();

public static MyClass getInstance(String name) {
    MyClass node = directory.get(name);
    if(node == null) {
       node == new MyClass(name);
    }
    return node;
}

或者同样,我可以有一个单独的MyClassFactory方法。

但我原本打算将MyClass子类化:

public class MySubClass extends MyClass;

如果我不再执行,并调用MySubClass.getInstance():

MyClass subclassObj = MySubClass.getInstance("new name");

...那么subclassObj将是一个普通的MyClass,而不是MySubClass。

然而,在每个子类中重写getInstance()似乎很容易。

我缺少一个简洁的解决方案吗?


这是问题的一般化版本。更具体,因为回答者要求他们。

该程序用于生成表示软件片段的节点之间的依赖关系的有向图。子类包括Java程序,Web服务,存储的SQL过程,消息驱动的触发器等。

因此,每个类在这个网络中都是“一个”元素,并且具有导航和修改与其他节点的依赖关系的方法。子类之间的区别在于用于从适当的源设置对象的populate()方法的实现。

假设名为'login.java'的节点知道它依赖'checkpasswd.sqlpl':

this.addDependency( NodeFactory.getInstance("checkpasswd.sqlpl"));

问题是checkpasswd.sqlpl对象此时可能已存在,也可能尚未存在。

7 个答案:

答案 0 :(得分:5)

你看过Guice了吗?不确定它是否能完全解决您的问题,但它充当通用工厂和依赖注入容器,并消除非类型安全的String键。

答案 1 :(得分:4)

静态方法是在父类上定义的,它也是静态调用的。因此,无法在方法中知道您在子类上调用它。 java编译器甚至可能静态地将调用解析为对父类的调用。

因此,您需要在建议时重新实现子类中的静态方法,或者使它们不是静态的,这样您就可以继承(在工厂对象的层次结构上,而不是类),或者传递参数以表示您要创建的类型。

查看EnumSet.noneOf()方法。它有一个类似的问题,它通过传递java.lang.Class方法解决它。您可以在类上使用newInstance。但就个人而言,我只使用工厂对象而不是静态方法的类。

答案 2 :(得分:2)

在阅读了你对这个问题的解释后,我认为你的图形结构中的子类很难对你自己造成困难。我认为如果将依赖图与“程序信息”

分开,问题就会变得简单得多

使用以下界面:

Public Interface Node<T> {
  public Object<T>    getObject();
  public String       getKey();
  public List<String> getDependencies();
  public void         addDependence(String dep);
}

然后使用工厂实例化您的节点

答案 3 :(得分:1)

Class类使用它可以创建的实例类型进行参数化。 (例如:Class&lt; String&gt;是String实例的工厂。)

我没有看到任何方法来了解当你使用工厂方法getOrCreate时应该创建的实例类型,所以我建议将它传递给方法并参数化生成的类型:

private static Map<String, MyClass> directory = new HashMap<String, MyClass>();

public static <T extends MyClass> T getInstance(String name, Class<T> generatorClass)
{
  MyClass node = directory.get(name);    
  if(node == null) {
    node = generatorClass.getConstructor(String.class).newInstance(name);
    directory.put(name, node);
  }
  return node;
}

另外,我注意到你实际上并没有把新构造的节点放在目录中 - 我假设这是一个疏忽。您也可以使用另一个没有生成器并硬编码为默认类型的方法重载此方法:

public static MyClass getInstance(String name) {
  return getInstance(name, MyClass.class);
}

答案 4 :(得分:1)

你似乎暗示在某个地方你知道如果不存在它应该是什么类。 如果在工厂中实现该逻辑,则应该获得正确的类。

这样你也不需要知道从工厂实际返回的是什么类。

如果您考虑使用Factory模式,我也可能会将'MyClass'设为接口。

答案 5 :(得分:0)

该模式似乎是一种Flyweight(在结构上,如果不是意图的完美匹配。)

如上所述,populate方法可以映射到Template模式,但不一定能解决所表达的问题。

我建议的是工厂的概括,create(而不是getInstance,这对我所期望的各种类型的方法确实意味着一个单身人士)。< / p>

public static MyClass createJavaNode(String name, <differentiator>);
public static MyClass createSqlPlNode (String name, <differentiator>);
.
.
.

有关name如何映射到<differentiator>的知识确实是一种实现选择。理想情况下,存在多态create,区分将是node类型。 create方法返回MyClass,但实际上是返回子类。我强烈考虑使用MyClass abstract方法(populate)将Template设置为接口或抽象类。

从描述中可以看出,创建行为在类型之间是不同的,而不是子类本身的行为,因此将MyClass重构为接口或抽象的情况上课变得更强。

用RA Heinlein来解释,TANSTAAFL - 作为免费午餐并不是没有这样的东西 - 某处应用程序中必须存在如何创建各种类型的知识。因此,您的选择是将这些知识放在Factory类中,正如其他一些答案所表达的那样,或者将 实现的创建决策与如何分开。创建。似乎有能力扫描文件系统并从中收集Node s。该代码将(或可以)知道应该创建的Node what 的答案)的类型

是以switch实现还是以更OO方式实现(表驱动调度的好处之一)取决于您。如果这可能是有用的,我可以在这里进行扩展。

BTW,如果需要为某些子类扩展或修改MyClass的核心行为,那么Decorator可能会有用。


原始答案:

您可以将DecoratorTemplate Design Patterns视为替代方案。

答案 6 :(得分:0)

你可能想要依赖注入。它会概括你要做的事情。

此外,继承可能不是您所需要的。除非你能通过“is-a”测试,否则永远不要使用它。如果你继承的类“Has-a”是唯一的字符串标记(基本上就是你的类看起来的那样),那并不意味着它“is-a”...

你可以让一个人握住另一个。可能有相当多的解决方案,但A)你没有发布你的整个需求列表,所以我们可以猜测,B)你可能想要的是依赖注入。 :)