我的目标是创建一组对象,每个对象都有一个唯一的标识符。如果已存在具有该标识符的对象,我想使用现有对象。否则我想创建一个新的。我试图不使用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对象此时可能已存在,也可能尚未存在。
静态方法是在父类上定义的,它也是静态调用的。所以,你无法在方法中知道你在子类上调用它。 java编译器甚至可能静态地将调用解析为对父类的调用。
因此,您需要在建议时重新实现子类中的静态方法,或者使它们不是静态的,这样您就可以继承(在工厂的层次结构上) 对象,而不是类),或传递参数来表示您要创建的类型。
查看EnumSet.noneOf()方法。它有一个类似的问题,它通过传递java.lang.Class方法解决它。你可以在类上使用newInstance。但就个人而言,我只使用工厂对象而不是静态方法的类。
你看过Guice了吗?不确定它是否能完全解决您的问题,但它充当通用工厂和依赖注入容器,并消除非类型安全的String键。
在阅读了你对这个问题的解释之后,我认为你的图形结构中的子类很难让你自己变得非常困难。我认为如果将依赖图与“程序信息”分开,问题会变得简单得多
使用如下界面:
Public Interface Node<T> {
public Object<T> getObject();
public String getKey();
public List<String> getDependencies();
public void addDependence(String dep);
}
然后使用工厂实例化您的节点
你似乎暗示在某个地方你知道如果不存在它应该是什么类。
如果在工厂中实现该逻辑,则应该获得正确的类。
这样你也不需要知道从工厂返回的实际类。
如果您考虑使用Factory模式,我也可能会将'MyClass'设为接口。
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);
}
你可能想要依赖注入。它会概括你要做的事情。
此外,继承可能不是您所需要的。除非你能通过“is-a”测试,否则永远不要使用它。如果你继承的类是“Has-a”唯一的字符串标签(基本上就是你的类似乎是什么),那并不意味着它“is-a”......
你可以让一个人握住另一个。可能有相当多的解决方案,但A)你没有发布你的整个需求列表,所以我们可以猜测,而B)你可能想要的是依赖注入。 :)
该模式似乎是一种 飞锤 (在结构上,如果不是意图的完美匹配。)
该 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
可能有用。
原答案:
你可能会考虑 装饰 要么 模板 设计模式 作为替代品。