【问题标题】:Polymorphic factory / getInstance() in JavaJava中的多态工厂/getInstance()
【发布时间】:2010-09-23 23:45:40
【问题描述】:

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

【问题讨论】:

  • 单身人士在哪里?我在这里没有看到一个。还假设getInstance() 方法中缺少return
  • 我添加了缺少的回报 - 谢谢。我想对于单例模式的变体必须有另一个名称,其中每个(类+标识符)保证唯一性
  • 看看这个stackoverflow.com/questions/11126866/…,它是一种利用 memoizer 模式的方法。它做你想做的事,而且它也是线程安全的!

标签: java design-patterns polymorphism factory


【解决方案1】:

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

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

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

【讨论】:

  • “可能”让我怀疑。看来你错了。编译器查看实例的声明类型以找到要调用的实际静态方法。这被称为“隐藏”而不是“覆盖”。 (-1)
【解决方案2】:

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

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

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

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

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

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

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

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

顺便说一句,如果MyClass 的核心行为需要针对某些子类进行扩展或修改,那么Decorator 可能会派上用场。


原答案:

您可以考虑将DecoratorTemplate Design Patterns 作为替代方案。

【讨论】:

  • 我很想知道这两种方法有什么帮助。你能扩展一下吗?
  • 很公平-也许如果您详细说明您要完成的工作,我可以给您更好的答案或将其删除。比如 MySubClass 给 MyClass 添加了什么?
  • 好的,谢谢。我试图概括,以免使问题陷入细节。但我现在将对其进行编辑以添加这些细节。
【解决方案3】:

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

【讨论】:

  • 好建议。大多数依赖注入/控制反转容器完全符合您的要求。
【解决方案4】:

Class 类使用它可以创建的实例类型进行参数化。 (例如:Class 是 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);
}

【讨论】:

    【解决方案5】:

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

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

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

    【讨论】:

      【解决方案6】:

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

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

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

      【讨论】:

        【解决方案7】:

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

        使用接口,例如:

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

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

        【讨论】:

        • +1 因为它符合我在阅读了其他人的好建议后做出的决定。
        猜你喜欢
        • 2011-03-17
        • 2013-09-24
        • 1970-01-01
        • 2014-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-09-24
        • 1970-01-01
        • 2011-05-08
        相关资源
        最近更新 更多