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
Short answer: Yes. Actually, since Java 8 you can provide default implementations for the methods in an interface and the only differences to an abstract class are constructors (which you don't use anyway apparently) and fields which should be private, so there's very little difference.
The use of reflection in the receiver entity of a design pattern enables the loose coupling required for transparent software evolution when unanticipated changes occur. Key to reflective design patterns is the absence of hard-coded references within the method code of both the delegates and the receiver.
Here are two additional consequences of the Factory Method pattern: Provides hooks for subclasses. Creating objects inside a class with a factory method is always more flexible than creating an object directly. Factory Method gives subclasses a hook for providing an extended version of an object.
When to Use Abstract Factory Pattern: The client is independent of how we create and compose the objects in the system. The system consists of multiple families of objects, and these families are designed to be used together. We need a run-time value to construct a particular dependency.
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
.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With