在工厂模式中使用反射 [已关闭]

2022-09-03 01:59:50

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

public class MyObjectFactory{
private Party party;

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

个人派对实施派对


答案 1

工厂模式的目的是将某些代码从它使用的对象的运行时类型中解耦:

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

...与简单地声明为类型有任何不同吗?最后,你的代码也是耦合的,但你已经放弃了静态类型验证。这意味着你得到的好处比没有好处要少,同时放弃了Java被强类型化的一些好处。如果您删除代码,您的代码仍将编译,您的IDE不会给您任何错误消息,并且您不会意识到此代码与运行时之间存在关系 - 这很糟糕。myPartycom.example.parties.SurprisePartycom.example.parties.SurprisePartycom.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(...)Stringname

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

...这是不可扩展的。由于此工厂创建的运行时类型的名称与参数的值之间存在明显的可观察关系,因此应考虑改用。这样,每次向系统添加新类型时,您的对象都不需要更改代码。nameClass.forNameFactoryParty


你可以考虑的其他事情是改用 AbstractFactory 模式。如果您的使用代码如下所示:

AbstractParty sParty = new PartyFactory().create("SurpriseParty");
AbstractParty gbParty = new PartyFactory().create("GoodByeParty");

...如果请求的经常发生的参与方类型数量有限,则应考虑为这些不同类型的参与方使用不同的方法:

public class PartyFactory {

    public Party getSurpriseParty() { ... }
    public Party getGoodByeParty() { ... }

}

...这将允许您利用Java的静态类型。

但是,此解决方案确实意味着每次添加新类型时都必须更改工厂对象 - 因此,反射式解决方案还是更好的解决方案实际上取决于您添加类型的频率和速度。每天都有新类型?使用反射。每十年都有一种新的政党类型?使用 .PartyAbstractFactoryPartyAbstractFactory


答案 2

以这种方式使用反射(Class.forName)几乎总是应用程序设计不良的标志。在某些类型中,它的使用是可以的,例如,如果您正在对外部库或插件进行某种动态加载。