Using Reflection in factory pattern [closed]

2019-03-24 15:40发布

问题:

Is it a good practice to use Reflection in Factory pattern?

public class MyObjectFactory{
private Party party;

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

PersonalParty implements Party

回答1:

The purpose of the factory pattern is to de-couple some code from the run-time type of an object it consumes:

// 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(...);

Using code like this, the PartyFactory is exclusively responsible for determining or knowing exactly what run-time type should be used.

You're forgoing that benefit by passing in the fully qualified name of the class you need. How is this...

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

... any different from simply declaring myParty as being of type com.example.parties.SurpriseParty? In the end your code is just as coupled, but you've given up static type verification. That's means you're incurring less than no benefit while surrendering some of the benefits of Java being strongly typed. If you delete com.example.parties.SurpriseParty your code will still compile, your IDE will give you no error messages and you won't realize there was a relationship between this code and com.example.parties.SurpriseParty until run time - that's bad.

At the very least, I'd advise you to at least change this code so the method's argument is a simple class name, not a fully qualified name:

// 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;
    }
}

Next: is it bad practice to use Class.forName(...)? That depends on what the alternative is, and the relationship between those String arguments (name) and the classes this factory will provide. If the alternative is a big conditional:

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

... that's not scalable. Since there is an obvious observable relationship between the names of the run-time types this factory creates and the value of the name argument, You should consider using Class.forName instead. That way your Factory object is protected from needing a code change every time you add a new Party type to the system.


Something else you could consider is using the AbstractFactory pattern instead. If your consuming code looks like this:

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

... where there are a limited number of often-occurring party types which are requested, you should consider having different methods for those different types of parties:

public class PartyFactory {

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

}

... which will allow you to leverage Java's static typing.

This solution does, however, mean that every time you add a new type of Party you have to change the factory object - so whether the reflective solution or the AbstractFactory is a better solution really depends on how often and how quickly you'll be adding Party types. A new type every day? Use reflection. A new party type every decade? Use an AbstractFactory.



回答2:

Using reflection it this way (Class.forName) is almost always sign of bad application design. There are some kinds, where its use is OK, for example if you are doing some kind of dynamic load of external libraries or plugins.



回答3:

You could use it for an API where you provide an API and an XML configfile where users can add the classnames of their plugin. Then, yes, you could use this