This is one of several JavaIdioms on the WikiWikiWeb
Problem
How can you parameterise the behaviour of a Java program based on external data?
Context
You often need to modify the behaviour of a program based on external data, such as a command-line parameter. If you hard-code a finite set of acceptable parameter values into a program you will have to modify the program whenever you need to add new parameters. The deployment of new versions will become more complex as the number of installed programs increases. If programs need to interpret parameters in order to interoperate (for example, if the parameters are read from network messages), you must ensure that all users upgrade before they can communicate, which becomes very difficult with large user bases.
Solution
Take advantage of Java's built-in facilities for dynamic loading and linking of code. Dynamically load and instantiate classes of behaviour corresponding to external parameters.
The behaviour of the Java program can be parameterised by simple values, such as short string names, or more complex values by creating a more elaborate scheme for mapping from parameters to classes.
A large, perhaps unbounded, range of behaviours can be described in this way.
Parameterisation does not impact runtime performance beyond the price of dynamically dispatched method calls.
New parameter values can be introduced without having to change the code that uses those values.
It is difficult to list all possible values, so that they can be displayed to the user, for example.
All ParameterClasses must be modified if the interface between the program and the ParameterClasses is changed.
Initialisation time is increased if the Reflection API must be used to query ParameterClasses for an appropriate Constructor.
If some ParameterClasses need to extend the plug-in interface, then you should define thoses extended interfaces as InterfacesIndependentOfImplementation.
Related Patterns
ParameterClasses is a JavaIdiom for mapping from a user-visible parameter to a class that implements the behaviour specified by that parameter. How that class is subsequently used is beyond the scope of this pattern. The class may be used as a StrategyPattern or a BridgePattern, or in some other manner.
This pattern uses the FactoryPattern in three distinct ways:
You can use InterfaceFactories to encapsulate the mapping from parameter values to class names.
Known Uses
The JavaIo package uses this idiom to name character encoding schemes that convert between Unicode characters and raw bytes.
The Java Media Framework (JMF) uses this idiom to specify codecs by media type and name sources of media.
The Java AWT uses this idiom to specify the GUI JavaAwtToolkit used by the JVM.
The Regent distributed programming environment uses this idiom to specify transport protocols for intercomponent bindngs.
NatPryce has written a compiler in which the back-end is implemented as ParameterClasses. Thus different code-generators can be loaded based on command-line options.
JDBC is almost like this, except there isn't a standard place to put JDBC drivers so they will automatically be loaded.
Applets work this way (the parameter is the URL.)
More Information
There is a very good introduction to Java class loaders and dynamic linking on the Java World web site at http://www.javaworld.com/javaworld/jw-01-1999/jw-01-techniques.html
Example
Even after reading all this, I'm not sure I understand the purpose of ParameterClasses. I guess a small sample of code could, like a drawing, save much time in explanations
Ok... let's use crypto as an example. We want to encrypt and decrypt data. There are a number of cryptographic algorithms (aka "ciphers") we can choose between, such as DES, Triple-DES or IDEA. We don't want to limit ourselves to a single cipher because, for example, our app will be decrypting encrypted mail where the sender will have chosen the cipher, and named the cipher in the mail headers. We also don't want to limit our app to a fixed set of ciphers, because new ciphers will be introduced as older ciphers are found to be insecure. We also don't want to limit our app to a single implementation for any cipher, because our users will want to choose a cipher implementation that they trust, and change implementations that they decide are untrustworthy. So, ciphers must be identified by parameters provided from external sources (mail messages), and these parameters must be mapped to implementations of the cipher (Java classes) that are dynamically loaded into our application.
Ciphers implement the Cipher interface that declares methods for encrypting and decrypting data, setting the encryption key, block size, initialisation vector, etc. For example:
interface Cipher { void reset(); byte[] encrypt( byte[] msg ) throws ...; byte[] decrypt( byte[] msg ) throws ...; ... etc ... }Ciphers are implemented as concrete classes that implement the Cipher interface. These are the ParameterClasses. They are dynamically loaded and instantiated when the application asks for a Cipher by name.
Ciphers are loaded by the newCipher method of the CipherFactory class. This method first maps the name of a cipher to a class, and then instantiates the class to create a new Cipher.
abstract class CipherFactory { public static Cipher newCipher( String name ) throws ... { return (Cipher) loadCipherClass( name ).newInstance(); } private static Class loadCipherClass( String cipher_name ) throws ... { return Class.forName( cipherNameToClassName(cipher_name) ); } private static String cipherNameToClassName( String cipher_name ) { return "com.examplecorp.cipher." + cipher_name.toLowerCase() + ".Cipher" + cipher_name; } }The CipherFactory translates a parameter into a class by loading a class named after the cipher with "Cipher" prepended, in a subpackage of "com.examplecorp.cipher" that has the same name as the cipher, but lowercase. E.g. the ParameterClass for the DES protocol would be com.examplecorp.cipher.des.CipherDES.
In practice, you would want to search for a class in a number of packages so that new packages of ciphers could be added to the system (e.g. search uk.mil.cipher and com.examplecorp.cipher), use a properties file to control how to find packages so that the class loading algorithm can be configured without recompiling the application, allow users to specify JAR files or URLs to search for classes, and so on.
--NatPryce
Discussion of what patterns this idiom applies moved to ParameterClassesPatterns.
This was in the Resulting Context, but I modified it up there. This is the rationale for that edit:
"Initialisation time is increased because objects must be instantiated and invoked via the Java Reflection API."
Reflection API is not required to invoke the methods of instances of ParameterClasses, due to step 1 of the solution. The parameter class itself must be dynamically loaded, and the Reflection API might be needed to find an appropriate constructor of the loaded class. Once an instance is created it is cast to a a well-known interface and invoked through that interface. --NatPryce
Im currently writing (in C++) a application that allows a sort of "visual programming" by connecting blocks together. The blocks are instances of classes written by the user. If only C++ had Java's ability to load classes on the fly and inspect the signature of their m ethods programmatically! I even considered doing it in Java instead of C++. But we want to use the app for number crunching, and sorry, Java just isn't fast enough for that.
So I did something in C++. It relies on the user writing a small piece of registration code for every class that is to be used. The solution relies rather heavily on templates to allow two functions to be connected if their types match, even if this type wasn't yet defined at the time the code that does the connecting was written.
I think it is a rather workable solution. Best of all: it is 100% Pure C++.
Stephan, consider the following:
This multi-language benchmark uses floating-point arithmetic to compute the "is the point inside the polygon" test. Here are the results (those with John Watton's paper can compare to the paper's results based on the performance of the first case).
=========+==============+============+=======+============ Language | Compiler | Options | Time | To gcc-O3 ---------+--------------+------------+-------+------------ C/C++ | gcc 2.7.2-O3 | (default) | 4716 |01.000 ---------+--------------+------------+-------+------------ Java | JDK 1.1.3 | -O, No JIT | 50663 |10.700 | | -O, JIT | 4467 |00.947 =========+==============+============+=======+============Hmmm... Java beats optimized gcc! -- RobertDiFalco
In my experience one can develop reliable code must faster in Java than in C++. I would suggest writing your application in Java and then using AlternateHardAndSoftLayers to integrate CPU intensive algorithms written C if performance is actually too slow in practice.
You can use ParameterClasses to select between native and Java implementations of your algorithms, so that your program loads the native version on platforms for which native code exists and the pure Java version on other platforms. I have used ParameterClasses for this very purpose in a network protocol framework, so that programs could use native interfaces to the TCP and UDP protocols if they existed or fall back on slower implementations that used the java.net package if native versions were not available.
--NatPryce
This idiom has turned out to be an extremely good fit with ExtensibleMarkupLanguage. For example, the configuration of Tomcat is done with XML files, and many of the parameters specify classes that implement an interface and are used for various services. For example, this snippet:
<RequestInterceptor className="org.apache.tomcat.request.SimpleRealm?" debug="0" />can be replaced by:
<RequestInterceptor className="org.apache.tomcat.request.JDBCRealm" debug="99" driverName="sun.jdbc.odbc.JdbcOdbcDriver" connectionURL="jdbc:odbc:TOMCAT" userTable="users" userNameCol="user_name" userCredCol="user_pass" userRoleTable="user_roles" roleNameCol="role_name" />The former implements a simple file-based authentication scheme, the latter implements authentication via a database. It is trivial to create your own authentication Realm by implementing RequestInterceptor (actually most likely extending the no-op BaseInterceptor? adaptor) and writing a different authenticate() method.
Notice the attributes -- they correspond to bean methods that the framework can call, and your class can be configured competely from the config file.
-- StevenNewton
This is exactly how the SceneBeans animation framework uses XML to load and configure JavaBeans into an animated SceneGraph.
In hindsight I think the name "ParameterClasses" is pretty poor. PluginClass? or NamedPluginClass? would be better, for example. I need a rename page mechanism that renames all links to a page. --NatPryce
This looks like a variation of FactoryPattern to me: You're creating instances of a set of classes, without tying yourself to the specific implementations of those classes. I might call it DynamicClassFactory? to emphasize its runtime behavior of dynamically loading classes to create instances. -- JeffGrigg
This pattern certainly uses the FactoryPattern, but also dynamic linking, which is beyond the scope of the FactoryPattern. In fact this idiom uses the FactoryPattern three distinct ways:
This idiom isn't limited to interpreting command line parameters, is it? No.
So what does it do?
Well, it...
Do we agree that it falls into the CreationalPatterns category? ...that it is a strategy for creating objects?
Yes, FactoryPattern/AbstractFactoryPattern and StrategyPattern are used to implement this pattern, but that doesn't imply that this pattern >is< one of its implementation details. You should use a name that is illustrative of the overall function. The new name may or may not have the words "factory" or "strategy" in it; try to find a name that works for people. -- JeffGrigg
I think you are concentrating too much on the implementation details of this pattern and not looking at the problem and context. Yes, the static structure solution is similar to that of strategy, factory, bridge and a load of other patterns. Let's face it, any design pattern involves only a few classes and there are only a finite number of ways those classes can be related, so all patterns look kind of similar if you ignore the problem and context.
If we can parameterize part of the class name, why not simply parameterize the entire class name? Actually this is what JDBC does. What the class name parameterization buys you is the controlled naming convention, probably more for the control of the package structure, but again this can be easily achieved by configure the allowable class names. So to me this is more of a "DynamicClassLoadingPattern?" derived from "AbstractFactoryPattern" instead of a somewhat more independent pattern. --HChen
You can do, if that makes sense for your application. But in doing so you lose a level of indirection, make the interface more complex to users and make it harder to parse data from programs written in other languages. Using the Cipher example, if I use a class name to identify ciphers I am (a) identifying cipher implementations, which stops me being able to choose a different implementation of the same cipher (b) force users to use class names for ciphers instead of the algorithm names that they are already used to (b) am unable to process encrypted mail messages because they use algorithm names to identify ciphers, not Java classes.
So the intent of the ParameterClasses pattern is to map from a user friendly parameter to the behaviour that is implied by that parameter and that is implemented by a Java class. Using a full class name as a parameter is a degenerate case of this pattern.
--NatPryce