2013-08-25 36 views
12

在工厂模式中使用反射是否是一种很好的做法?在工厂模式中使用反射

public class MyObjectFactory{ 
private Party party; 

public Party getObject(String fullyqualifiedPath) 
{ 
    Class c = Class.forName(fullyqualifiedPath); 
    party = (PersonalParty)c.newInstance(); 
    return party; 
} 
} 

PersonalParty实现党

+1

这是否有任何严重的用例。一般来说,我会考虑反思作为我的最后手段。特别是为了这个目的。 –

+0

JSf2.0这样的框架如何使用注释?它们应该使用Reflection API在运行时实例化对象? – Ullas

+1

如果是关于JSF,那么请标签是这样的。 –

回答

9

工厂模式的目的是去耦合一些代码从它消耗对象的运行时类型:

// 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"); 

...从简单的声明myPartycom.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代替。这样,每次向系统添加新的Party类型时,您的Factory对象都不需要更改代码。


你可以考虑的其他东西是使用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

+0

感谢您的详细解释Sir .. – Ullas

+0

@乌拉斯 - 我的荣幸。增加了一个段落,强调何时应该使用反射解决方案,而不是何时应该使用'AsbtractFactory'。 –

1

使用反射这种方式(以Class.forName)几乎总是不好的应用程序设计的标志。有一些类型,如果你正在做某种外部库或插件的动态加载,那么它的使用是可以的。

1

您可以将它用于提供API和XML配置文件的API,用户可以在其中添加插件的类名。然后,是的,你可以使用这个