问题 Java中的多态工厂/ getInstance()


我的目标是创建一组对象,每个对象都有一个唯一的标识符。如果已存在具有该标识符的对象,我想使用现有对象。否则我想创建一个新的。我试图不使用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过程,消息驱动的触发器等。

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

假设名为“login.java”的节点得知它依赖于“checkpasswd.sqlpl”:

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

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


5185
2017-11-25 16:05


起源

单身人士在哪里?我在这里看不到一个。人们还假设有一个缺失 return 在里面 getInstance() 方法。 - Ken Gentle
我添加了遗失的回复 - 谢谢。我想必须有一个模式的另一个名称,它是Singleton的一个变体,其中每个(class + identifier)保证唯一性 - slim
看看这个 stackoverflow.com/questions/11126866/... 这是一种利用这种方法的方法 memoizer 模式。它做你想要的,它也是线程安全的! - mike


答案:


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

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

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


4
2017-11-25 16:12



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


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


5
2017-11-25 16:35



好建议。大多数依赖注入/反转控制容器完全符合您的要求。 - erickson


在阅读了你对这个问题的解释之后,我认为你的图形结构中的子类很难让你自己变得非常困难。我认为如果将依赖图与“程序信息”分开,问题会变得简单得多

使用如下界面:

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

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


2
2017-11-27 09:48



+1因为它符合我在阅读其他人的好建议后所做出的决定。 - slim


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

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

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


1
2017-11-25 16:44





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);
}

1
2017-11-25 16:36





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

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

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


0
2017-11-25 18:09





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

populate 如上所述,方法可以映射到 Template 模式,虽然它不一定解决表达的担忧。

我建议的是工厂的概括,用 create (代替 getInstance,无论如何,这对我所期望的各种类型都意味着一个单身人士。

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 接口或抽象类变得更强大。

用R. A. Heinlein的话来解释 TANSTAAFL  - 没有免费午餐这样的事 - 某处 应用程序中必须存在如何创建各种类型的知识。因此,您的选择是将这些知识放在Factory类中,就像其他一些答案所表达的那样,或者将决定分开 什么 实现是从 怎么样 它被创造了。似乎有扫描文件系统和收集文件系统的能力 Node是的。该代码将(或可以)知道 类型 的 Node (答案是 什么)应该创建。

是否实现为 switch 或者以更OO的方式(表驱动发送的好处之一)取决于你。如果这可能是有用的,我可以在这里进行扩展。

BTW,如果是核心行为 MyClass 需要为某些子类进行扩展或修改,即a Decorator 可能有用。


原答案:

你可能会考虑 装饰 要么 模板  设计模式 作为替代品。


0
2017-11-25 16:17



我很难看到其中任何一个有帮助。你可以扩展吗? - slim
足够公平 - 也许如果你详细说明你想要完成什么,我可以给你一个更好的答案或删除它。就像MySubClass添加到MyClass一样? - Ken Gentle
好,谢谢。我试图概括,以免在具体细节中陷入困境。但我现在要编辑它以添加这些细节。 - slim