Last time I gave a brief overview of some of the Reflection utilities available in Java, with the promise to discuss how to rethink a common design pattern using the capabilities of reflection: the Factory Pattern. The brief wiki article I linked lists three main limitations:
- Existing code is broken and must be refactored.
- Classes cannot be extended because of the private constructors
- or, if they are made protected, classes must re-implement factory methods, but this will not produce objects of the appropriate type, because the signatures must match.
The first problem is a design issue that cannot be avoided—any implementation of new features or fundamental changes to a program will require that the entire program be updated, the addition of a new design pattern is no different. I consider this “limitation” to be merely one of stubbornness with legacy code, not a fault of the design approach
The second problem can be avoided by causing the third problem, but this is a faux problem when using reflection, so we will address how to solve this one specifically.
There exist two reasonable factory implementations: a factory where the objects being produced are all known explicitly at compile time, and the object creation mechanism can exist externally to the objects (as a part of the factory itself—this is how the ImageReader example works in the wiki), and a slightly more useful framework type of example, where the Factory class is used to pass off creation to a static construction method, which is reminiscient of the Strategy pattern. The first pattern doesn’t suffer from a problem with a lack of factory method implementations, because the singular factory method exists externally to the objects being produced—adding another object to the list is as simple as adding another type constant/enum and expanding a switch statement.
The second method is initially scary, because it revolves around dealing with the unknown. An object of dubious origins must be instantiated without any way to declare it in the source code. Security concerns aside, this should be screaming to you for a Class object and the use of reflection.
Let’s say that you are constructing an application framework that consists of a bunch of small parts, lets call them Parts, for simplicity, that must be independently developed and distributed. At runtime your application can have Parts loaded at runtime by other Parts, or they can be loaded based on a configuration file that is read at load time. The only feasible way to do this loading is to provide a centralized method implemented by the framework to ensure that each Part is loaded correctly, or else many things could go wrong if you encounter a poorly written Part, which is out of your control, but brings the system bearing your logo to a crashing halt. Congratulations, you’ve arrived at the factory pattern. Now it’s obvious that these classes will be needed:
class PartFactory {
public Part create(...);
}
class Part {
public static Part factoryCreate(PartInfo pi) { return null; }
}
class PartInfo {
// Information for new Parts here...
}
The arguments to the factory create() method are left up to you—you can use configuration files that store class names to load when initializing Parts (which is what I did in a recent project), or you can have some other method of determining what to load (might I suggest using a ClassLoader-like approach, whereby the functionality for determining what to load is also replaceable by user-created parts).
Assuming that the appropriate class name to load is generated by some other mechanism of the Part loader and then passed in to the create() function, and the PartInfo class is also created elsewhere (could be stored serialized in an object), this is a reasonable starting point:
public Part create(String name, PartInfo pi) {
Class> c;
Method m;
try {
c = Class.forName(name);
m = c.getMethod("factoryCreate", pi.getClass());
return (Part) m.invoke(null, pi);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Is that it? Pretty much. By using the strategy pattern and data encapsulation, there is only one factory method that is used to create new Parts. Because a Part subclass must override this method in order to be instantiated, there are no dangling factory constructor methods that need to be cleaned up by subclasses in order to be complete. Of course, I have not completely addressed the real problem with the factory pattern, issue number 3 above. This problem is really almost irreconcilable. A band-aid can be applied to it by enforcing some sort of constructor signature matching (like in PHP 6) and pulling some reflection tricks to get the most specific constructor, but it severely limits the application.
Finally, some more support for this approach, consisting of a factory/strategy/reflection combination: JavaBeans. I haven’t had much experience using them, but a quick glance at the JavaBeans trail shows that the Beans themselves are centered around the use of reflection to enable dynamic application functionality, and even take it a step further and using a specialized reflection-powered analyzer, the Introspector class, to find out many layers of relevant information about a JavaBean.
Like
0 Responses
Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.