在工厂模式中使用Reflection是一个好习惯吗?
public class MyObjectFactory{
private Party party;
public Party getObject(String fullyqualifiedPath)
{
Class c = Class.forName(fullyqualifiedPath);
party = (PersonalParty)c.newInstance();
return party;
}
}
PersonalParty实施Party
答案 0 :(得分:13)
工厂模式的目的是将某些代码与其消耗的对象的运行时类型分离:
// This code doesn't need to know that the factory is returning
// an object of type `com.example.parties.SurpriseParty`
AbstractParty myParty = new PartyFactory().create(...);
使用这样的代码,PartyFactory
专门负责确定或确切知道应该使用哪种运行时类型。
您通过传递所需课程的完全限定名称来放弃该优势。这是怎么回事......
// This code obviously DOES know that the factory is returning
// an object of type `com.example.parties.SurpriseParty`.
// Now only the compiler doesn't know or enforce that relationship.
AbstractParty myParty = new PartyFactory().create("com.example.parties.SurpriseParty");
...与简单地声明myParty
属于com.example.parties.SurpriseParty
类型有什么不同?最后你的代码就像耦合一样,但是你已经放弃了静态类型验证。这意味着在放弃强类型Java的一些好处的同时,您所获得的收益甚至没有任何好处。如果你删除com.example.parties.SurpriseParty
你的代码仍然会编译,你的IDE将不会给你任何错误消息,你不会意识到这段代码与com.example.parties.SurpriseParty
之间存在关系直到运行时 - 这很糟糕。
至少,我建议您至少更改此代码,以便方法的参数是一个简单的类名,而不是完全限定的名称:
// I took the liberty of renaming this class and it's only method
public class MyPartyFactory{
public Party create(String name)
{
//TODO: sanitize `name` - check it contains no `.` characters
Class c = Class.forName("com.example.parties."+name);
// I'm going to take for granted that I don't have to explain how or why `party` shouldn't be an instance variable.
Party party = (PersonalParty)c.newInstance();
return party;
}
}
下一篇:使用Class.forName(...)
是不好的做法?这取决于替代方案是什么,以及那些String
参数(name
)与此工厂将提供的类之间的关系。如果替代方案是一个很大的条件:
if("SurpriseParty".equals(name) {
return new com.example.parties.SurpriseParty();
}
else if("GoodbyeParty".equals(name)) {
return new com.example.parties.GoodbyeParty();
}
else if("PartyOfFive".equals(name)) {
return new com.example.parties.PartyOfFive();
}
else if(/* ... */) {
// ...
}
// etc, etc etc
......那是不可扩展的。由于此工厂创建的运行时类型的名称与name
参数的值之间存在明显的可观察关系,因此您应该考虑使用Class.forName
。这样,每次向系统添加新的Factory
类型时,都会保护您的Party
对象不需要更改代码。
您可以考虑的其他方法是使用AbstractFactory
模式。如果您的消费代码如下所示:
AbstractParty sParty = new PartyFactory().create("SurpriseParty");
AbstractParty gbParty = new PartyFactory().create("GoodByeParty");
......如果请求的常常派对类型数量有限,您应考虑为不同类型的派对设置不同的方法:
public class PartyFactory {
public Party getSurpriseParty() { ... }
public Party getGoodByeParty() { ... }
}
...这将允许您利用Java的静态类型。
但是,此解决方案确实意味着每次添加新类型的Party
时都必须更改工厂对象 - 因此反射解决方案或AbstractFactory
是否是更好的解决方案关于添加Party
类型的频率和速度。每天都有新型?使用反射。每十年举办一次新派对?使用AbstractFactory
。
答案 1 :(得分:1)
以这种方式使用反射(Class.forName)几乎总是标志着糟糕的应用程序设计。有一些类型,其使用是可以的,例如,如果您正在进行某种动态加载的外部库或插件。
答案 2 :(得分:1)
您可以将它用于API,您可以在其中提供API和XML配置文件,用户可以在其中添加其插件的类名。然后,是的,你可以使用这个