Basic design patterns implemented with the JaxMe JavaSource framework |
The package net.sf.jaxme.js.patterns contains some demonstrations of basic design patterns implemented with the JaxMe JavaSource framework.
A proxy class is a class that implements a list of interfaces specified at
compile time. The proxy object typically holds a reference to an internal
object that implements the same interfaces (or parts of them). The proxy
object is implemented by delegating method calls to the internal object.
This is the same principle as implemented by the class
java.lang.reflect.Proxy
, which created proxy objects dynamically
at runtime using Java reflection.
Compared to the standard Proxy class, the generator has the obvious disadvantage, that you have to specify the implemented interfaces at runtime. On the other hand, it allows both to select the proxy objects super class and the derivation of subclasses. In fact the derivation of a subclass is much more obvious, simple and faster than the use of an InvocationHandler.
The proxy generator is implemented by the class ProxyGenerator. Use of the ProxyGenerator is demonstrated in the Ant target "generate.proxy".
Multiple inheritance is a design pattern which is not so easy to implement
in Java - unless you use the ProxyGenerator.
This is demonstrated by the JUnit test
MultipleInheritanceTest,
which creates a subclass of Typesafe enumerations are classes with a private constructor and only
a few defined instances. Each instance does have a name and a value. For
example, within Ant you might use the following lines:
The proxy chain is best explained with the situation it was made for.
Suggest a really complex source generator with varying options. For example,
a child element of an object can have multiplicity zero, one or more: In
the latter case the child element will most probably be represented by a
list. Quite necessarily the source generator will sonner or later be
bloated with statements like
java.util.Observable
, that also inherits
from java.util.ArrayList
. The example implements a list, which
notifies its observers whenever the add()
method is invoked.
Typesafe enumerations
<enumGenerator targetClass="net.sf.jaxme.js.junit.EnumExample"
destDir="${build.src}">
<item name="JOE" value="John Doe"/>
<item name="POPEYE" value="Olivia's Lover"/>
<item name="DONALD" value="The Duck King"/>
</enumGenerator>
This will generate a class looking roughly like this:
public class EnumExample {
private String name;
private String value;
private EnumExample(String pName, String pValue) {
name = pName;
value = pValue;
}
public String getName() { return name; }
public String getValue() { return value; }
public final static EnumExample JOE = new EnumExample("JOE", "John Doe");
public final static EnumExample POPEYE = new EnumExample("POPEYE", "Olivia's Lover");
public final static EnumExample DONALD = new EnumExample("DONALD", "The Duck King");
}
The important thing with this enumeration is that there cannot be other instances
besides JOE, POPEYE and DONALD.
Proxy chains (also known as delegator pattern)
if (child.isMultiple()) {
// Do something, working with a List
...
} else {
// Do something else, working with a single element
}
Even worse, the code will in both cases look quite similar: As soon as the
list item is fetched and casted to a proper type, this is just like working
with a single item. As time goes by, more and more similar options come into
play: Localization, varying datatypes, and so on. Soon you start to think of
better solutions.
The first attempt is typically subclassing. For example, if you have a source generator for the case multiplicity zero or one, it can well be expanded to the general case, with multiplicity two or more being handled in the subclass. Localization is handled in the next subclass, and so on. Your code looks much better now and becomes maintainable again.
However, time goes by, and more options are added to the source generator: Transaction handling, encoding issues, and so on. Soon you find yourself in trouble again: The subclassing approach doesn't work so well anymore, because in a subclass chain E -> D -> C -> B -> A (where B is a subclass of A, C is a subclass of B, and so on) you sometimes want all of them, but sometimes only D -> C -> A or E -> A. At that point of thinking a new approach was born: The event chain.
The idea is replacing the subclasses with an event interface. For example, suggest that the source generator might contain code like the following:
public class FooGeneratorImpl implements FooGenerator { public JavaSource getFooClass() { JavaSource js = factory.newJavaSource(fooClassName, "public"); getXMethod(js); getYMethod(js); getZMethod(js); } ... }The corresponding interface might look like this:
public interface FooGenerator { public JavaSource getFooClass(); public JavaMethod getXMethod(JavaSource js); public JavaMethod getYMethod(JavaSource js); public JavaMethod getZMethod(JavaSource js); }If you take this methods as events, then you might well write a default class A implementing the interface. The other classes are implemented as subclasses of an automatically generated proxy class. For example, the class B might just add another method to the
Foo
.
This might look like the following:
public class B extends FooGeneratorProxy { // Override only the getFooClass() method, all other methods are // passed to the base generator by the proxy. public JavaSource getFooClass() { JavaSource result = super.getFooClass(); JavaMethod bMethod = result.newJavaMethod("bMethod", "void", "public"); return result; } }
Likewise, the C class might change the interface of method X, and so on. Any feature is implemented by a single class, which you can optionally add to the chain (turning the feature on) or remove from the chain (turning the feature off).
However, there's still a problem left: When you are inside A (the topmost
class) and do a getXMethod()
, then you call your own class and
not the chains bottom. This problem is fixed by the following design:
|
|
The ChainedFooGenerator
is exactly matching the FooGenerator
interface, the exception being an additional parameter FooGenerator pController
in all methods. The FooGenerator interface can be created automatically, also an
implementation of FooGenerator calling the first element in the chain of
ChainedFooGenerator
implementations. The manually created classes
have to be changed slightly, here's the updated FooGeneratorImpl
:
public class FooGeneratorImpl implements ChainedFooGenerator { public JavaSource getFooClass(FooGenerator pController) { JavaSource js = factory.newJavaSource(fooClassName, "public"); pController.getXMethod(js); pController.getYMethod(js); pController.getZMethod(js); } ... }
Likewise, here is the updated B class:
public class B extends ChainedFooGeneratorProxy { // Override only the getFooClass() method, all other methods are // passed to the base generator by the proxy. public JavaSource getFooClass(FooGenerator pController) { JavaSource result = super.getFooClass(pController); JavaMethod bMethod = result.newJavaMethod("bMethod", "void", "public"); return result; } }
The proxy chain pattern is implemented by the ChainGenerator. From within Ant, it looks like the following:
<chainGenerator destDir="src"> <chain controllerInterfaceName="com.dcx.sein.dbtk.generator.javasg.IModelSG" chainInterfaceName="com.dcx.sein.dbtk.generator.javasg.auto.IChainedModelSG" proxyClassName="com.dcx.sein.dbtk.generator.javasg.auto.ChainedModelSGProxy" implementationClassName="com.dcx.sein.dbtk.generator.javasg.auto.ModelSGImpl"/> <chain controllerInterfaceName="com.dcx.sein.dbtk.generator.javasg.IObjectSG" chainInterfaceName="com.dcx.sein.dbtk.generator.javasg.auto.IChainedObjectSG" proxyClassName="com.dcx.sein.dbtk.generator.javasg.auto.ChainedObjectSGProxy" implementationClassName="com.dcx.sein.dbtk.generator.javasg.auto.ObjectSGImpl"/> </chainGenerator>
The controllerInterfaceName
is the name of the basic interface.
This is what you actually want to use from the outside. The controller interface
must be available as a compiled class, because it is inspected with Java reflection.
The other classes are generated: The chainInterfaceName
is the interface
being implemented by the manually written classes. The proxyClassName
is an automatically generated implementation of the chainInterface
,
which passes all events to the next element in the chain. And the
implementationClassName
is an also automatically generated implementation
of controllerInterface
, that works internally by passing all events
to the first element in the chain.