Parameter Classes

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.

  1. Define an interface through which to access the parameterised behaviour
  2. Define a naming scheme that maps user-specified parameters to the name of a Java class.
  3. Define classes that implement the interface for various parameters, and follow the naming scheme so that they can be found by the parameter-to-name mapping.
  4. At run time, given a parameter value, map it to the name of the Java class that performs the required behaviour.
  5. Dynamically load that class using the Class.forName method.
  6. Instantiate an object of the class using the Class.newInstance method or via a Constructor of the class.
  7. Cast the object to the interface you have defined and pass it to the objects that need to use the parameterised behaviour.

Resulting Context

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 may need to EncapsulateMultiStageConstruction of parameter objects, or TestWhetherInConstructionPhase when initialising them.

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


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


Discussion of what patterns this idiom applies moved to ParameterClassesPatterns.


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++.

-- StephanHouben

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


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[] ) throws ...;
	byte[] decrypt( byte[] ) 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 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


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:

-- NatPryce

This idiom isn't limited to interpreting command line parameters, is it? No.

So what does it do?

Well, it...

Looks like a factory to me. Now it is significantly different from the "factory" patterns found in the GangOfFour DesignPatterns book, and close relatives: No one is passing you an instance that will "do the appropriate creation" for you.

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


EditText of this page (last edited December 20, 2003)
FindPage by browsing or searching

This page mirrored in JavaIdioms as of April 29, 2006