11

我的目标是创建一组对象,每个对象都有一个唯一标识符。如果具有该标识符的对象已经存在,我想使用现有对象。否则我想创建一个新的。我尽量不使用 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 对象此时可能已经存在,也可能不存在。

4

7 回答 7

5

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

于 2008-11-25T16:35:23.290 回答
4

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

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

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

于 2008-11-25T16:12:59.043 回答
2

在阅读了您对问题的解释后,我认为您通过在图形构造中进行子分类使自己变得非常困难。我认为如果将依赖图与“程序信息”分开,问题会变得更加简单

使用接口,例如:

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

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

于 2008-11-27T09:48:16.527 回答
1

Class 类使用它可以创建的实例类型进行参数化。(例如:Class<String> 是 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);
}
于 2008-11-25T16:36:01.133 回答
1

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

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

如果您正在考虑使用工厂模式,我也可能会将“MyClass”作为接口。

于 2008-11-25T16:44:58.463 回答
0

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

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

我建议对工厂进行概括,对您期望的各种类型使用create(而不是,这对我来说确实意味着单例)方法。getInstance

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

关于 a 如何name映射到 a的知识<differentiator>实际上是一种实现选择。理想情况下,会有一个多态性create,并且区分是按node类型。该create方法返回MyClass,但实际上是返回子类。我强烈考虑使用方法(有)制作MyClass接口或抽象类。abstract populateTemplate

从描述来看,确实是类型之间的创建行为不同,而不是子类本身的行为不同,因此重构为MyClass接口或抽象类的情况变得更加强大。

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

是否以更面向对象的方式实现switch还是以更面向对象的方式实现(表驱动调度会很好的时代之一)取决于您。如果这可能有用,我可以在这里扩展。

MyClass顺便说一句,如果需要扩展或修改某些子类的核心行为,那么 aDecorator可能有用。


原答案:

您可能会考虑将装饰器模板 设计模式作为替代方案。

于 2008-11-25T16:17:36.257 回答
0

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

此外,继承可能也不完全是您所需要的。除非您能通过“is-a”测试,否则切勿使用它。如果您继承的类“Has-a”唯一的字符串标签(本质上就是您的类似乎是什么),这并不意味着它“is-a”......

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

于 2008-11-25T18:09:27.577 回答