Java 中的多态工厂/getInstance()
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/317869/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Polymorphic factory / getInstance() in Java
提问by slim
I'm aiming to create a set of objects, each of which has a unique identifier. If an object already exists with that identifier, I want to use the existing object. Otherwise I want to create a new one. I'm trying not to use the word Singleton, because I know it's a dirty word here...
我的目标是创建一组对象,每个对象都有一个唯一的标识符。如果具有该标识符的对象已存在,我想使用现有对象。否则我想创建一个新的。我尽量不使用单身这个词,因为我知道这是一个肮脏的词......
I can use a factory method:
我可以使用工厂方法:
// 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;
}
Or equally, I could have a separate MyClassFactory method.
或者同样,我可以有一个单独的 MyClassFactory 方法。
But I had intended to subclass MyClass:
但我本来打算将 MyClass 子类化:
public class MySubClass extends MyClass;
If I do no more, and invoke MySubClass.getInstance():
如果我不再这样做,并调用 MySubClass.getInstance():
MyClass subclassObj = MySubClass.getInstance("new name");
... then subclassObj will be a plain MyClass, not a MySubClass.
...然后 subclassObj 将是一个普通的 MyClass,而不是一个 MySubClass。
Yet overriding getInstance() in every subclass seems hacky.
然而,在每个子类中覆盖 getInstance() 似乎很麻烦。
Is there a neat solution I'm missing?
有我想念的巧妙解决方案吗?
That's the generalised version of the question. More specifics, since the answerers asked for them.
这是问题的概括版本。更具体的,因为回答者要求他们。
The program is for generating a directed graph of dependencies between nodes representing pieces of software. Subclasses include Java programs, Web Services, Stored SQL procedures, message-driven triggers, etc.
该程序用于生成表示软件块的节点之间的依赖关系的有向图。子类包括 Java 程序、Web 服务、存储 SQL 过程、消息驱动触发器等。
So each class "is-a" element in this network, and has methods to navigate and modify dependency relationships with other nodes. The difference between the subclasses will be the implementation of the populate()method used to set up the object from the appropriate source.
因此,该网络中的每个类“都是一个”元素,并且具有导航和修改与其他节点的依赖关系的方法。子类之间的区别在于populate()用于从适当的源设置对象的方法的实现。
Let's say the node named 'login.java' learns that it has a dependency on 'checkpasswd.sqlpl':
假设名为“login.java”的节点了解到它依赖于“checkpasswd.sqlpl”:
this.addDependency( NodeFactory.getInstance("checkpasswd.sqlpl"));
The issue is that the checkpasswd.sqlpl object may or may not already exist at this time.
问题是 checkpasswd.sqlpl 对象此时可能已经存在,也可能不存在。
采纳答案by Wouter Lievens
The static method is defined on the parent class, and it's called statically as well. So, there's no way of knowing in the method that you've called it on the subclass. The java compiler probably even resolves the call statically to a call to the parent class.
静态方法是在父类上定义的,它也是静态调用的。因此,无法在方法中知道您在子类上调用了它。java 编译器甚至可能将调用静态解析为对父类的调用。
So you will need to either reimplement the static method in your child classes as you propose, or make them not static so you can inheritance (on a hierarchy of factory objects, not classes), or pass a parameter to signify the type you want to create.
因此,您需要按照您的建议在子类中重新实现静态方法,或者使它们不是静态的,以便您可以继承(在工厂对象的层次结构上,而不是类上),或者传递一个参数来表示您想要的类型创建。
Check out the EnumSet.noneOf() method. It has a similar issue as you do, and it solves it by passing the java.lang.Class method. You could use newInstance on the class. But personally, I'd just use factory objects rather than classes with static methods.
查看 EnumSet.noneOf() 方法。它和你有类似的问题,它通过传递 java.lang.Class 方法来解决它。您可以在类上使用 newInstance 。但就我个人而言,我只是使用工厂对象而不是带有静态方法的类。
回答by bajafresh4life
Have you looked into Guice? Not sure if it would solve your problem exactly, but it acts as a generic factory and dependency injection container, and eliminates non-type safe String keys.
你研究过Guice吗?不确定它是否能完全解决您的问题,但它充当通用工厂和依赖注入容器,并消除了非类型安全的 String 键。
回答by pvgoddijn
after reading you explanation of the problem i think your making it very difficult for yourself by sub classing in your graph construction. i think the problem becomes much more simple if you separate the dependency graph from the "program information"
在阅读了您对问题的解释后,我认为您通过在图形构造中进行子类化而使自己变得非常困难。我认为如果将依赖关系图与“程序信息”分开,问题会变得更简单
use an Interface such as:
使用接口,例如:
Public Interface Node<T> {
public Object<T> getObject();
public String getKey();
public List<String> getDependencies();
public void addDependence(String dep);
}
and then use a factory to instantiate you nodes
然后使用工厂来实例化你的节点
回答by Alex Miller
The Class class is parameterized with the type of instance it can create. (ex: Class<String> is a factory for String instances.)
Class 类使用它可以创建的实例类型进行参数化。(例如:Class<String> 是 String 实例的工厂。)
I don't see any way to get around knowing the type of instance that should be created when you getOrCreate using the factory method here, so I would recommend passing it to the method and parameterizing on the type being generated:
我没有看到任何方法可以让您了解在使用工厂方法 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;
}
Also, I noticed you weren't actually putting the newly constructed nodes in the directory - I'm assuming that's an oversight. You could also overload this method with another that didn't take a generator and hardcoded to the default type:
另外,我注意到您实际上并没有将新构建的节点放在目录中 - 我假设这是一个疏忽。您还可以使用另一个不带生成器并硬编码为默认类型的方法重载此方法:
public static MyClass getInstance(String name) {
return getInstance(name, MyClass.class);
}
回答by pvgoddijn
You seem to imply that somewhere you know what class it should be if it doesn't exist. If you implement that logic in your factory you should get the correct classes.
你似乎暗示在某个地方你知道它应该是什么类,如果它不存在。如果您在工厂中实现该逻辑,您应该获得正确的类。
This way you should also have no need to know what class actually was returned from the factory.
这样你就不需要知道从工厂实际返回了什么类。
I would also likely make 'MyClass' an interface if you're considering a Factory pattern.
如果您正在考虑工厂模式,我也可能会将“MyClass”作为一个接口。
回答by Bill K
You probably want dependency injection. It would generalize what you are trying to do somewhat.
你可能想要依赖注入。它会概括您正在尝试做的事情。
Also, Inheritance is probably not exactly what you need either. Never use it unless you can pass the "is-a" test. If your inherited class "Has-a" unique string tag (essentially that's what your class seems to be) it doesn't mean it "is-a"...
此外,继承可能也不是您所需要的。除非您可以通过“is-a”测试,否则切勿使用它。如果您继承的类“具有-a”唯一字符串标记(本质上这就是您的类似乎是什么),这并不意味着它“是-a”...
You could have one hold the other though. There are probably quite a few solutions but A) you didn't post your entire list of requirements so we can but guess, and B) what you probably want is dependency injection. :)
不过,你可以让一个持有另一个。可能有很多解决方案,但是 A)您没有发布完整的需求列表,所以我们只能猜测,B)您可能想要的是依赖注入。:)
回答by Ken Gentle
The pattern appears to be a sort of Flyweight(structurally, if not a perfect match for intent.)
该模式似乎是一种享元(结构上,如果不是意图的完美匹配。)
The populatemethod, as described, could be mapped to the Templatepattern, although it doesn't necessarily address the expressed concerns.
如上所述populate,该方法可以映射到Template模式,尽管它不一定解决所表达的问题。
What I'd suggest is a generalization of the factory, with a create(instead of getInstance, which does imply a Singleton, to me anyway) method for the various types you expect.
我建议的是对工厂的概括,对于您期望的各种类型,使用create(而不是getInstance,这确实意味着单例,对我来说)方法。
public static MyClass createJavaNode(String name, <differentiator>);
public static MyClass createSqlPlNode (String name, <differentiator>);
.
.
.
The knowledge about how a namemaps to a <differentiator>is really an implementation choice. Ideally, there'd be a polymorphic createand the differentiation would be by nodetype. The createmethod returns MyClass, but is really returning the subclasses. I'd strongly consider making MyClasseither an interface or an abstract class, with an abstractpopulatemethod (there's the Template).
关于如何name映射到 a的知识<differentiator>实际上是一种实现选择。理想情况下,会有一个多态性create,并且区分是按node类型进行的。该create方法返回MyClass,但实际上返回子类。我强烈考虑MyClass使用abstractpopulate方法(有Template)制作接口或抽象类。
From the description, it really appears that it is the creation behavior that is different between the types, not the behavior of the sub-classes themselves, so the case for the refactoring of MyClassto an interface or abstract class gets stronger.
从描述上看,确实是类型之间的创建行为不同,而不是子类本身的行为,因此重构为MyClass接口或抽象类的情况变得更强。
To paraphrase R. A. Heinlein, TANSTAAFL- There Ain't No Such Thing As A Free Lunch -- somewherethe knowledge of how to create the various types has to exist in the application. So your choice is to put that knowledge in the Factory class, as some of the other answers have expressed, or to separate the decision of whatimplementation is created from howit is created. It seems like there is a capability of scanning a file system and collecting the Nodes from it. That code will (or can) know the typeof the Node(the answer to the what) that should be created.
套用RA海因莱因,TANSTAAFL-不过是没有这样的东西作为免费的午餐-地方如何创建各种类型的具有在应用中存在的知识。所以,你的选择是把这些知识在工厂类,因为一些其他的答案都表示,或分开的决定是什么,从创建执行如何创建它。似乎有一种扫描文件系统并Node从中收集s的能力。该代码将(或可以)知道应该创建的(对what的答案)的类型。Node
Whether that gets implemented as a switchor in a more OO fashion (one of the times where table driven dispatch would be nice) is up to you. If this is something that might be useful, I can expand on that here.
是否以一种switch或更多的面向对象的方式实现(表驱动调度会很好的时间之一)取决于您。如果这可能有用,我可以在此处进行扩展。
BTW, if the core behavior of MyClassneeds to be extended or modified for some subclasses, that is where a Decoratormight be useful.
顺便说一句,如果MyClass需要为某些子类扩展或修改的核心行为,这就是 aDecorator可能有用的地方。
Original Answer:
原答案:
You might consider the Decoratoror TemplateDesign Patternsas alternatives.

