8. Detailed Evaluation
A language that does not affect the way
you think about programming, is not worth knowing.
Alan J. Perlis

This chapter presents the individual "Gang of Four" pattern implementations and corresponding evaluations. The patterns are presented in the order defined by Gamma et al. To keep this thesis as short as possible, the detailed pattern evaluations are kept to a minimum using the evaluation format defined in the evaluation approach. Each pattern has a dedicated section that presents its Intent, Structure, Participants, and Implementation, using familiar "Gang of Four" pattern element names. We present the pattern structure as a detailed UML class diagram that includes information about relevant attributes and operations. The UML diagrams also identifies the pattern participants, which are described thereafter, linked to the developed Java classes. An Implementation section addresses all functionality described by Gamma et al. in the Implementation and Sample Code elements for a given pattern, and illustrates whether or not the functionality can be implemented in Java 6. The paragraph headings in the Implementation sections are taken from the corresponding topics discussed by Gamma et al. in the pattern descriptions. A brief summary concludes this chapter.

8.1.  Creational Patterns

8.1.1.  Abstract Factory
Abstract Factory is a Creational pattern with Object scope. The implementation is located in the dk.rode.thesis.abstractfactory package.

8.1.1.1.  Intent
Provide an interface for creating families of related or dependent objects without specifying their concrete classes [Gamma95, p.87].

8.1.1.2.  Structure
Figure 8.1 illustrates the Abstract Factory implementation as an UML Class diagram, where the pattern participants can also be identified. The pattern participants are described in the next section.

Figure 8.1PrintFigure 8.1Abstract Factory UML Class diagram
Click for larger Abstract Factory UML Class diagram

8.1.1.3.  Participants
Table 8.1 describes the Abstract Factory participants in the words of Gamma et al. [Gamma95, p.89] and lists the corresponding implementations developed in the evaluation.

Table 8.1PrintTable 8.1Abstract Factory participants
ParticipantDescriptionImplementation
AbstractFactory
  • Declares an interface for operations that create abstract product objects.
AbstractionFactory<E>, GeneratorFactory<E,P>, SequenceFactory<E,P>, PrototypicalFactory<T>, factorymethod.Factory<T>
ConcreteFactory
  • Implements the operations to create concrete product types.
PrototypicalRegistry, StandardFactory<E,P>, StandardAbstractionFactory<E>, SynchronisedAbstractionFactory<E>, MemorizableAbstractionFactory<E>, PrototypicalAbstractionFactory<E>, CollectionValueFactory<E>, RangeValueFactory, PrototypicalSequenceFactory<E>, sub—classes of Factory<T> and PrototypicalFactory<T>
AbstractProduct
  • Declares an interface for a type of product object.
bridge.SequenceAbstraction<E>, bridge.SequenceValueGenerator<E>, meta.model.Sequence<E>
ConcreteProduct
  • Defines a product object to be created by the corresponding concrete factory, and implements the AbstractProduct interface.
facade.FibonacciSequence, and all bridge.SequenceAbstraction<E> and bridge.SequenceValueGenerator<E> implementations
Client
  • Uses only interfaces declared by AbstractFactory and AbstractProduct types.
Main

8.1.1.4.  Implementation
Factories as singletons Design choice. Creating the products Factory Method is used in all implementations of SequenceFactory<E,P>. Prototype usage is illustrated in PrototypicalFactory<T>. A prototypical registry capable of creating any number of prototypical products is implemented as PrototypicalRegistry. Type literals are preferred over class literals to create products because they support generic types, for example using factorymethod.Factory<T>. Defining extensible factories Parameterised product creation is illustrated in factorymethod.CommandCreator<E,T> and GeneratorFactory<E,P>. The first illustrates use of arguments to determine the product type, while the latter use arguments required by the product type. The PrototypicalRegistry class shows how to create unrelated product types in a type safe manner that does not require an unsafe down—cast as in C++.

8.1.2.  Builder
Builder is a Creational pattern with Object scope. The dk.rode.thesis.builder package contains the implementation.

8.1.2.1.  Intent
Separate the construction of a complex object from its representation so that the same construction process can create different representations [Gamma95, p.97].

8.1.2.2.  Structure
Figure 8.2 illustrates the Builder implementation as an UML Class diagram, where the pattern participants can also be identified. The pattern participants are described in the next section.

Figure 8.2PrintFigure 8.2Builder UML Class diagram
Click for larger Builder UML Class diagram

8.1.2.3.  Participants
Table 8.2 describes the Builder participants in the words of Gamma et al. [Gamma95, p.98-99] and lists the corresponding implementations developed in the evaluation.

Table 8.2PrintTable 8.2Builder participants
ParticipantDescriptionImplementation
Builder
  • Specifies an abstract interface for creating parts of a Product object.
ExpressionBuilder<E>, ComparableExpressionBuilder<E>
ConcreteBuilder
  • Constructs and assembles parts of the product by implementing the Builder interface.
  • Defines and keeps track of the representation it creates.
  • Provides an interface for retrieving the product.
StandardExpressionBuilder<E>, StandardComparableExpressionBuilder<E>, CountingExpressionBuilder<E>, CountingComparableExpressionBuilder<E>, TypedExpressionBuilder<E>, TypedComparableExpressionBuilder<E>
Director
  • Constructs an object using the Builder interface.
Main
Product
  • Represents the complex object under construction. ConcreteBuilder builds the product's internal representation and defines the process by which it is assembled.
  • Includes classes that define the constituent parts, including interfaces for assembling the parts into the final result.
All interpreter.Expression<E> implementations, including interpreter.TypedExpression<E> types

8.1.2.4.  Implementation
Assembly and construction interface Design choice. The ExpressionBuilder<E> type builds products in a tree—like structure. Various build methods accept already constructed expressions, while some constructed expressions can be manipulated directly, e.g. interpreter.FlowExpression<E>. Why no abstract class for products? Design choice. The Builder implementation use covariant return types to express specific types when required. Empty methods as default in builder Design choice. In our view, however, empty methods constitute a poor design choice that will inevitable lead to problems in form of runtime errors, e.g. null pointers. Empty methods are must better suited for Template Method with primitive operations that do not return a value the context depends on.

8.1.3.  Factory Method
Factory Method is a Creational pattern with Class scope. The dk.rode.thesis.factorymethod package contains the implementation.

8.1.3.1.  Intent
Separate the construction of a complex object from its representation so that the same construction process can create different representations [Gamma95, p.97].

8.1.3.2.  Structure
Figure 8.3 illustrates the Factory Method implementation as an UML Class diagram, where the pattern participants can also be identified. The pattern participants are described in the next section.

Figure 8.3PrintFigure 8.3Factory Method UML Class diagram
Click for larger Factory Method UML Class diagram

8.1.3.3.  Participants
Table 8.3 describes the Factory Method participants in the words of Gamma et al. [Gamma95, p.108-109] and lists the corresponding implementations developed in the evaluation.

Table 8.3PrintTable 8.3Factory Method participants
ParticipantDescriptionImplementation
Product
  • Defines the interface of objects the factory method creates.
command.Command<E>
ConcreteProduct
  • Implements the Product interface.
All command.Command<E> implementations
Creator
  • Declares the factory method, which returns an object of type Product. Creator may also define a default implementation of the factory method that returns a default ConcreteProduct object.
  • May call the factory method to create a Product object.
CommandCreator<E,T>, Factory<T>, TypedFactory<T,P>
ConcreteCreator
  • Overrides the factory method to return an instance of a ConcreteProduct.
SequenceCommandCreator<E>, ReversibleSequenceCommandCreator<E>, ReflectiveCommandCreator<E>, EvilSequenceCommandCreator<E>, sub—classes of Factory<T> or TypedFactory<T,P>

8.1.3.4.  Implementation
Two major varieties The CommandCreator<E,T> class provide a default implementation, but requires sub—classes to implement the abstract factory method. This is design choice; sub—classes could simply return null from the implemented factory method and the default would always be used. Parameterised factory methods CommandCreator<E,T> illustrates parameterised factory methods, including call to super (default) implementations. T is the token used to determine the product type (command) to create. Language specific variants and issues Type literals are preferred over class literals to create products because they support generic types, for example using Factory<T>. The abstractfactory.PrototypicalRegistry class stores prototypes based on their classes. In Template Method, templatemethod.SequenceTemplate<K,E> illustrates that, unlike C++, it is possible to invoke sub—class hooks from the constructor. Lazy initialisation is used in Facade, which only creates sequences used for calculation on demand in a thread—safe manner. Using templates to avoid sub—classing The Factory<T> class can create any type in a type—safe fashion, including generic types, providing T supplies an applicable constructor. The abstractfactory.PrototypicalRegistry uses upper bounded type parameters to ensure that all type parameters are copyable since the new operator cannot be invoked on type parameters because of erasure. Naming conventions Design choice, but important if overloaded factory methods are used. Java cannot overload on generic types, only raw types. The Visitor implementation shows how naming can allow visitation based on type parameters.

8.1.4.  Prototype
Prototype is a Creational pattern with Object scope. The dk.rode.thesis.prototype package contains the implementation.

8.1.4.1.  Intent
Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype [Gamma95, p.117].

8.1.4.2.  Structure
Figure 8.4 illustrates the Prototype implementation as an UML Class diagram, where the pattern participants can also be identified. The pattern participants are described in the next section.

Figure 8.4PrintFigure 8.4Prototype UML Class diagram
Click for larger Prototype UML Class diagram

8.1.4.3.  Participants
Table 8.4 describes the Prototype participants in the words of Gamma et al. [Gamma95, p.119] and lists the corresponding implementations developed in the evaluation.

Table 8.4PrintTable 8.4Prototype participants
ParticipantDescriptionImplementation
Prototype
  • Declares an interface for cloning itself.
Copyable<T>, StrictCopyable<T>
ConcretePrototype
  • Implements the operation for cloning itself.
SymbolSequence and CountdownSequence, but also all other meta.model.Sequence<E> implementations
Client
  • Creates a new object by asking a prototype to clone itself.
Main

8.1.4.4.  Implementation
Using a prototype manager The abstractfactory.PrototypicalRegistry class is a prototype manager. It also covers the Smalltalk example supplied in the Sample Code element [Gamma95, p.125]. Implementing the clone operation The SymbolSequence class uses Java's built—in clone facility as well as the prototypical functionality offered by StrictCopyable.copy(). The copy() method used in interpreter.Expression<E> objects may encounter unmanageable cyclic references in expression trees. However, the Expression.asSymbol(Context) method illustrates that Java easily can handle cyclic references in graph traversals, so it is simply a matter of implementation. All Copyable<T> objects supply copy constructors by convention to perform deep—copy. Initialising clones The Copyable.copy() method is parameter—less, but could employ arguments in a manner similar to factorymethod.Factory<T>. It is a design choice we do not recommend because it shifts focus more towards factories, but at flag to indicate deep copying may be appropriate. A copy of an uninitialised interpreter.FlowExpression<E> instance will have to be initialised before use.

8.1.5.  Singleton
Singleton is a Creational pattern with Object scope. The dk.rode.thesis.singleton package contains the implementation.

8.1.5.1.  Intent
Ensure a class only has one instance, and provide a global point of access to it [Gamma95, p.127].

8.1.5.2.  Structure
Figure 8.5 illustrates the Singleton implementation as an UML Class diagram, where the pattern participants can also be identified. The pattern participants are described in the next section.

Figure 8.5PrintFigure 8.5Singleton UML Class diagram
Click for larger Singleton UML Class diagram

8.1.5.3.  Participants
Table 8.5 describes the Singleton participants in the words of Gamma et al. [Gamma95, p.119] and lists the corresponding implementations developed in the evaluation.

Table 8.5PrintTable 8.5Singleton participants
ParticipantDescriptionImplementation
Singleton
  • Defines an instance operation that lets clients access its unique instance. instance is a class operation.
  • May be responsible for creating its own unique instance.
DanishAlphabetSequence, NorwegianAlphabetSequence, SimpsonsFamilySequence, SimpsonsAndBouvierFamilySequence, MutatedSimpsonsFamilySequence, SmileySequence

Though not described as a participant, Gamma et al. thoroughly describe and illustrate the use of singleton registries [Gamma95, p.130-132] in the Implementation element. Hence, singleton registries are also implemented in the evaluation. The name and minimal description of the "pseudo—participants" as listed in table 8.6 below are not defined by Gamma et al.

Table 8.6PrintTable 8.6 — Additional Singleton entities
NameDescriptionImplementation
SingletonRegistry
  • Defines an operation to return Singleton instances.
  • May defer creation of a Singleton instance to the singleton type itself.
SingletonRegistry<T>

@Singleton
ConcreteSingletonRegistry
  • Implements the SingletonRegistry interface.
  • May be a Singleton.
StatelessSingletonRegistry<T>, StatefullSingletonRegistry<T>, LoadableSingletonRegistry<T>

Notice, that we do not demand that a registry must store the different singleton types.

8.1.5.4.  Implementation
Ensuring a unique instance The DanishAlphabetSequence is implemented as a single enumeration constant. The SimpsonsFamilySequence uses a static method to lazily create the singleton instance in a thread—safe manner that does not require synchronisation. Both rely on automatic initialisation, but only when requested. Of the three problematic issues related to this in C++ [Gamma95, p.129-130], only parameterised singleton methods cannot be done in this fashion. They must be implemented as static synchronised methods. Though a parameterised singleton method is a design choice, we do not recommend it as it obfuscates the Singleton purpose. What happens if the singleton method is invoked again with a different value, for example. Overriding new as in Smalltalk is not possible in Java. Sub—classing the Singleton class The meta.log.LogFactory class uses a system property to determine the type of log to use. Using conditional statements to decide the class is trivial. Java has no separation between header and object files, so compile—time linking to a different implementation is changing the entire class. The SimpsonsFamilySequence allows for sub—classing of the actual Singleton type, which to our knowledge is a novel approach. It does so by identifying the caller to simulate C++ friends, but in a type—safe fashion. A Singleton Registry as described by Gamma et al. [Gamma95, p.130-132] is defined via the SingletonRegistry<T> interface. The LoadableSingletonRegistry<T> is an implementation that allows for type—safe dynamic loading of singleton types based on class names. The actual singleton creation is deferred to the singleton types themselves and no registration is required. This is clearly more flexible than the canonical implementation.

8.2.  Structural Patterns

8.2.1.  Adapter
Adapter is a Structural pattern with both Class and Object scope. The implementation is located in the dk.rode.thesis.adapter package.

8.2.1.1.  Intent
Convert the interface of a class into another interface clients expect. Adapter lets classes work together that could not otherwise because of incompatible interfaces [Gamma95, p.139].

8.2.1.2.  Structure
Figure 8.6 illustrates the Adapter implementation as an UML Class diagram, where the pattern participants can also be identified. The pattern participants are described in the next section.

Figure 8.6PrintFigure 8.6Adapter UML Class diagram
Click for larger Adapter UML Class diagram

8.2.1.3.  Participants
Table 8.7 describes the Adapter participants in the words of Gamma et al. [Gamma95, p.141] and lists the corresponding implementations developed in the evaluation.

Table 8.7PrintTable 8.7Adapter participants
ParticipantDescriptionImplementation
Target
  • Defines the domain—specific interface that Client use.
meta.model.Sequence<E>, java.util.Iterator<E>
Client
  • Collaborates with objects conforming to the Target interface.
Main
Adaptee
  • Defines an existing interface that needs adapting.
Any Sequence<E> implementation
Adapter
  • Adapts the interface of Adaptee to the Target interface.
SequenceAdapter<E,T> (using AdapterDelegate<S,T>), IteratorSequence<E>

8.2.1.4.  Implementation
Implementing class adapters in C++ Java does not support multiple inheritance and thus not class adapters. Dynamic proxies can be used to simulate private implementation as illustrated in the meta.reflect.proxy.ProxyFactory class, but it still requires composition as in object adapters. Pluggable adapters a) The set of abstract operations to use is a design choice. Sub—class implementation for adapter functionality is used by any adapter that utilises inner classes such as the proxy.SequenceProxyFactory class. b) The SequenceAdapter<E,T> class uses composition and forwards requests to the adaptee (sequence) stored internally. c) The SequenceAdapter<E,T> is parameterised and uses AdapterDelegate<S,T> instances.

8.2.2.  Bridge
Bridge is a Structural pattern with Object scope. The dk.rode.thesis.bridge package contains the implementation.

8.2.2.1.  Intent
Decouple an abstraction from its implementation so that the two can vary independently [Gamma95, p.151].

8.2.2.2.  Structure
Figure 8.7 illustrates the Bridge implementation as an UML Class diagram, where the pattern participants can also be identified. The pattern participants are described in the next section.

Figure 8.7PrintFigure 8.7Bridge UML Class diagram
Click for larger Bridge UML Class diagram

8.2.2.3.  Participants
Table 8.8 describes the Bridge participants in the words of Gamma et al. [Gamma95, p.154] and lists the corresponding implementations developed in the evaluation.

Table 8.8PrintTable 8.8Bridge participants
ParticipantDescriptionImplementation
Abstraction
  • Defines the abstraction's interface.
  • Maintains a reference to an object of type Implementor.
SequenceAbstraction<E>
RefinedAbstraction
  • Extends the interface defined by Abstraction.
SynchronisedSequenceAbstraction<E>, MemorizableSequenceAbstraction<E>
Implementor
  • Defines the interface for implementation classes. This interface does not have to correspond exactly to Abstraction's interface; in fact, the two interfaces can be quite different. Typically, the Implementor interface provides only primitive operations, and Abstraction defines higher—level operations based on these primitives.
SequenceValueGenerator<E>

SequenceValueCollection<E,C>, SequenceValueSet<E,C>
ConcreteImplementor
  • Implements the Implementor interface and defines its concrete implementation.
SequenceValueArrayList<E>, SequenceValueHashSet<E>, SequenceValueLinkedHashSet<E>, SequenceValueTreeSet<E>, SequenceValueRange

8.2.2.4.  Implementation
Only one implementor Use of a single Implementor is a design choice. Java scope rules allow implementations to be hidden from clients or by using anonymous adapters for the Implementor implementation. Creating the right Implementor object Design choice. Sharing implementors The Main (test) class in the Bridge implementation utilises the meta.reflect.proxy.ProxyFactory to manage shared implementations. The sharing is handled via the Handle/Body idiom implemented in Java using dynamic proxies. Using multiple inheritance Java does not support multiple inheritance, but as Gamma et al. also note, this binds the implementation to the abstraction at compile—time. Composition can be used instead as illustrated by the SequenceAbstraction<E> class, which can be either fixed or changeable.

8.2.3.  Composite
Composite is a Structural pattern with Object scope. The dk.rode.thesis.composite package contains the implementation.

8.2.3.1.  Intent
Compose objects into tree structures to represent part—whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly [Gamma95, p.163].

8.2.3.2.  Structure
Figure 8.8 illustrates the Composite implementation as an UML Class diagram, where the pattern participants can also be identified. The pattern participants are described in the next section.

Figure 8.8PrintFigure 8.8Composite UML Class diagram
Click for larger Composite UML Class diagram

8.2.3.3.  Participants
Table 8.9 describes the Composite participants in the words of Gamma et al. [Gamma95, p.165] and lists the corresponding implementations developed in the evaluation.

Table 8.9PrintTable 8.9Composite participants
ParticipantDescriptionImplementation
Component
  • Declares the interface for objects in the composition.
  • Implements default behaviour for the interface common to all classes, as appropriate.
  • Declares an interface for accessing and managing its child components.
  • Defines an interface for accessing a component's parent in the recursive structure, and implements it if that is appropriate (optional).
meta.model.Sequence<E>
Leaf
  • Represents a leaf object in the composition. A leaf has no children.
  • Defines behaviour for primitive objects in the composition.
Any Sequence<E> implementation
Composite
  • Defines behaviour for components having children.
  • Stores child components.
  • Implements child—related operations in the Component interface.
CompositeSequence<E>, AbstractCompositeSequence<E>, CharSequenceCompositeSequence

CompositeStrategy
Client
  • Manipulates objects in the composition through the Component interface.
Main

8.2.3.4.  Implementation
Explicit parent references Design choice; not implemented here. Sharing components Design choice. Maximizing the Component interface Design choice. Declaring the child management operations The Composite implementation opts for type safety over transparency as discussed by Gamma et al. [Gamma95, p.167-168]. Child management is defined in the Composite (CompositeSequence<E>) participant because it can never make sense for Leaf components (Sequence<E>). This makes sense in Java because unlike C++, the instanceof operator provides a safe way to cast to a given type. Should Component implement a list of Components? Children are stored in the Composite participant. Child ordering The CompositeStrategy enumeration defines strategies for traversing the composite structure, such as depth—first and breath—first. Caching to improve performance Design choice. The strategies defined here allow retrieved lists to be reused without the need to traverse the composite structure again. Who should delete components? Java employs garbage collection. What is the best data structure for storing components? This is a design choice. AbstractCompositeSequence<E> uses a java.util.RandomAccess list for fast traversal.

8.2.4.  Decorator
Decorator is a Structural pattern with Object scope. The dk.rode.thesis.decorator package contains the implementation.

8.2.4.1.  Intent
Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to sub—classing for extending functionality [Gamma95, p.175].

8.2.4.2.  Structure
Figure 8.9 illustrates the Decorator implementation as an UML Class diagram, where the pattern participants can also be identified. The pattern participants are described in the next section.

Figure 8.9PrintFigure 8.9Decorator UML Class diagram
Click for larger Decorator UML Class diagram

8.2.4.3.  Participants
Table 8.10 describes the Decorator participants in the words of Gamma et al. [Gamma95, p.177] and lists the corresponding implementations developed in the evaluation.

Table 8.10PrintTable 8.10Decorator participants
ParticipantDescriptionImplementation
Component
  • Defines the interface for objects that can have responsibilities added to them dynamically.
meta.model.Sequence<E>
ConcreteComponent
  • Defines an object to which additional responsibilities can be attached.
Any Sequence<E> implementation
Decorator
  • Maintains a reference to a Component object and defines an interface that conforms to Component's interface.
SequenceDecorator<E>
ConcreteDecorator
  • Adds responsibilities to the Component.
AppenderDecorator, DuplexDecorator, UppercaseDecorator

The SequenceDecorator<E> class is used in several other pattern implementations as well, including Iterator, Proxy, and Visitor.

8.2.4.4.  Implementation
Interface conformance Implementing a common interface is sufficient in Java; inheritance is not required. SequenceDecorator<E> implements Sequence<E> and inherits meta.model.AbstractSequence<E>, but can decorate any sequence type. Omitting the abstract decorator class Design choice. Keeping Component classes lightweight Design choice, but one that cannot always be controlled in case API classes, for example, have to be decorated. Changing the skin of an object vs. changing the guts Design choice. Abstract decorator types can make it easier to implement multiple concrete decorator types as illustrated with the abstract SequenceDecorator<E> class.

8.2.5.  Facade
Facade is Structural with Object scope. The dk.rode.thesis.facade package contains the implementation.

8.2.5.1.  Intent
Provide a unified interface to a set of interfaces in a sub—system. Facade defines a higher—level interface that makes the sub—system easier to use [Gamma95, p.185].

8.2.5.2.  Structure
Figure 8.10 illustrates the Facade implementation as an UML Class diagram, where the pattern participants can also be identified. The pattern participants are described in the next section.

Figure 8.10PrintFigure 8.10Facade UML Class diagram
Click for larger Facade UML Class diagram

8.2.5.3.  Participants
Table 8.11 describes the Facade participants in the words of Gamma et al. [Gamma95, p.187] and lists the corresponding implementations developed in the evaluation.

Table 8.11PrintTable 8.11Facade participants
ParticipantDescriptionImplementation
Facade
  • Knows which sub—system classes are responsible for a request.
  • Delegates client requests to appropriate sub—system objects.
MathFacade
Subsystem Class
  • Implement sub—system functionality.
  • Handle work assigned by Facade object.
  • Have no knowledge of the facade; that is, they keep no reference to it.
AckermannSequence, FibonacciSequence, RandomSequence, UnboundedRandomSequence, iterator.IterableSequence<E>, state.ReversiblePrimeSequence

8.2.5.4.  Implementation
Reducing client/sub—system coupling Design choice, but may require factory creation. The MathFacade class is implemented as a Singleton. Public vs. private sub—system classes Design choice. Packages in Java can provide encapsulation and implicitly information hiding (access modifiers) for sub—systems. MathFacade, FibonacciSequence, and RandomSequence represent public classes, while AckermannSequence and UnboundedRandomSequence are private sub—system classes and are as such declared package private. Sub—system classes from other packages must be public to be utilised.

8.2.6.  Flyweight
Flyweight is a Structural pattern with Object scope. The dk.rode.thesis.flyweight package contains the implementation.

8.2.6.1.  Intent
Use sharing to support large numbers of fine—grained objects efficiently [Gamma95, p.195].

8.2.6.2.  Structure
Figure 8.11 illustrates the Flyweight implementation as an UML Class diagram, where the pattern participants can also be identified. The pattern participants are described in the next section.

Figure 8.11PrintFigure 8.11Flyweight UML Class diagram
Click for larger Flyweight UML Class diagram

8.2.6.3.  Participants
Table 8.12 describes the Flyweight participants in the words of Gamma et al. [Gamma95, p.198-199] and lists the corresponding implementations developed in the evaluation.

Table 8.12PrintTable 8.12Flyweight participants
ParticipantDescriptionImplementation
Flyweight
  • Declares an interface through which flyweights can receive and act on extrinsic state.
Textual<T>

Character
ConcreteFlyweight
  • Implements the Flyweight interface and adds storage for intrinsic state, if any. A ConcreteFlyweight object must be sharable. Any state it stores must be intrinsic; that is, it must be independent of the ConcreteFlyweight object's context.
Word, AbstractCharacter, Letter, Symbol, Whitespace
UnsharedConcreteFlyweight
  • Not all Flyweight sub—classes need to be shared. The Flyweight interface enables sharing; it does not enforce it. It is common for UnsharedConcreteFlyweight objects to have ConcreteFlyweight objects as children at some level in the flyweight object structure.
Sentence
FlyweightFactory
  • Creates and manages flyweight objects.
  • Ensures that flyweights are shared properly. When a client requests a flyweight, the FlyweightFactory object supplies an existing instance or creates one, if none exists.
CharacterFactory
Client
  • Maintains a reference to flyweight(s).
  • Computes or stores the extrinsic state of flyweight(s).
Main

8.2.6.4.  Implementation
Removing extrinsic state Design choice. Here, java.util.Locale represents extrinsic state for sentence structures, because it rarely makes sense to store a locale with each character, word, or even sentence. Managing shared objects The CharacterFactory class uses a java.util.HashMap<K,V> to store flyweights, which are created on demand only in a thread—safe manner. The java.lang.Object.hashCode() and java.lang.Object.equals(Object) methods makes it very easy to handle flyweights using the Java Collections framework. Purging of old flyweights is a design choice.

8.2.7.  Proxy
Proxy is a Structural pattern with Object scope. The implementation is located in the dk.rode.thesis.proxy package.

8.2.7.1.  Intent
Provide a surrogate placeholder for another object to control access to it [Gamma95, p.207].

8.2.7.2.  Structure
Figure 8.12 illustrates the Proxy implementation as an UML Class diagram, where the pattern participants can also be identified. The pattern participants are described in the next section.

Figure 8.12PrintFigure 8.12Proxy UML Class diagram
Click for larger Proxy UML Class diagram

8.2.7.3.  Participants
Table 8.13 describes the Proxy participants in the words of Gamma et al. [Gamma95, p.209-210] and lists the corresponding implementations developed in the evaluation.

Table 8.13PrintTable 8.13Proxy participants
ParticipantDescriptionImplementation
Proxy
  • Maintains a reference that lets the proxy access the real subject. Proxy may refer to a Subject if the RealSubject and Subject interfaces are the same.
  • Provides an interface identical to Subject's so that a proxy can be substituted for the real subject.
  • Controls access to the real subject and may be responsible for creating and deleting it.
  • Other responsibilities depend on the kind of proxy (remote proxy, virtual proxy, protection proxy, or smart reference).
SynchronisedSequence<E>, NonResettableSequence<E>, ImmutableSequence<E>, java.lang.reflect.Proxy objects

(SequenceProxyFactory, meta.reflect.proxy.ProxyFactory)
Subject
  • Defines the common interface for RealSubject and Proxy so that a Proxy can be used anywhere a RealSubject is expected.
meta.model.Sequence<E>
RealSubject
  • Defines the real object that the proxy represents.
Any Sequence<E> implementation

8.2.7.4.  Implementation
Overloading the member access operator in C++ Java does not support operator overloading, but dynamic proxies can simulate the behaviour. The SequenceProxyFactory.getVirtualSequence(..) method returns a virtual (dynamic) proxy that will not create an actual Sequence<E> instance until a method declared in the Sequence<E> interface is invoked. Methods declared in other types can still be invoked, for example java.lang.Object.toString(). However, the actual class of the proxied sequence cannot be used for casts or instanceof tests using the proxy, since the class is java.lang.reflect.Proxy. This can be a severe limitation on dynamic proxy usage since the client will not (necessarily) know a proxy is accessed in place of the real object. As java.lang.Object.equals(Object), for example, utilises instanceof, this can be a problem for object comparison and collection usage. This also affects the Java Handle/Body idiom used to manage shared objects acquired from meta.reflect.proxy.ProxyFactory. Using doesNotUnderstand in Smalltalk Not possible in Java 6, since the signature of a method to be invoked is determined at compile—time, while only the actual type of the (polymorphic) object is determined at runtime [Sierra06, p.111]. Reflection does not allow creation of new methods at runtime, so only compile—time known methods can be invoked. However, method names (strings) can be used to identify methods, but will require an elaborate "framework" to fetch methods, dispatch if found, and error handling if not found ("does not understand"). This principle is used in several dynamic proxy implementations. Proxy does not always have to know the type of the RealSubject The virtual sequence described above uses type literals to supply the generic type to be created. The protection proxies defined operate on interfaces, for example ImmutableSequence<E> that makes any Sequence<E> type immutable.

8.3.  Behavioural Patterns

8.3.1.  Chain of Responsibility
Chain of Responsibility is a Behavioural pattern with Object scope. The implementation is located in the dk.rode.thesis.chainofresponsibility package.

8.3.1.1.  Intent
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it [Gamma95, p.223].

8.3.1.2.  Structure
Figure 8.13 illustrates the Chain of Responsibility implementation as an UML Class diagram, where the pattern participants can also be identified. The pattern participants are described in the next section.

Figure 8.13PrintFigure 8.13Chain of Responsibility UML Class diagram
Click for larger Chain of Responsibility UML Class diagram

8.3.1.3.  Participants
Table 8.14 describes the Chain of Responsibility participants in the words of Gamma et al. [Gamma95, p.225-226] and lists the corresponding implementations developed in the evaluation.

Table 8.14PrintTable 8.14Chain of Responsibility participants
ParticipantDescriptionImplementation
Handler
  • Defines an interface for handling requests.
  • Implements the successor link (optional).
Handler<R>
ConcreteHandler
  • Handles requests it is responsible for.
  • Can access its successor.
  • If the ConcreteHandler can handle the request, it does so; otherwise it forwards the request to its successor.
CharacterHandler

LetterHandler, LetterCaseHandler, SymbolHandler, WhitespaceHandler
Client
  • Initiates the request to a ConcreteHandler object on the chain.
Main

The implementation makes the chain explicit, represented by the HandlerChain<R> class. Ergo, the name and minimal description of the "pseudo—participants" as listed in table 8.15 below are not defined by Gamma et al.

Table 8.15PrintTable 8.15 — Additional Chain of Responsibility entities
NameDescriptionImplementation
HandlerChain
  • Maintains a chain of ConcreteHandler objects, which may be altered at any time.
  • Manages the forwarding of reqests to ConcreteHandler objects until a matching handler is found, if any.
  • Declares operations for accessing and removing handlers.
  • Doubles as a HandlerLink.
HandlerChain<R>

AbstractHandlerChain<R>, StandardHandlerChain<R>, WeakHandlerChain<R>
HandlerLink
  • Forwards an unhandled request to the next ConcreteHandler represented by the link. The link may represent a ConcreteHandler or a HandlerChain.
HandlerLink<R>

8.3.1.4.  Implementation
Implementing the successor chain The implementation differs from the canonical implementation by Gamma et al. in that it makes the chain explicit, represented by the HandlerChain<R> type. This means a concrete handler can only access its successor through the chain as it does not store a reference to it itself. This reduces coupling between handlers; handlers can participate in several chains; and allow a chain to be altered at any time by adding or removing handlers to and from it. Connecting successors The HandlerLink<R> type represents a link to the next handler in the chain, if any. Representing requests The type of request is specified using generics in the Handler<R> class, where R is the type of request. This is compile—time type—safe and flexible. Automatic forwarding in Smalltalk Not supported in Java (but see section 8.2.7.4).

8.3.2.  Command
Command is a Behavioural pattern with Object scope. The dk.rode.thesis.command package contains the implementation.

8.3.2.1.  Intent
Encapsulate a request as an object, thereby letting you parameterise clients with different requests, queue or log requests, and support undoable operations [Gamma95, p.233].

8.3.2.2.  Structure
Figure 8.14 illustrates the Command implementation as an UML Class diagram, where the pattern participants can also be identified. The pattern participants are described in the next section.

Figure 8.14PrintFigure 8.14Command UML Class diagram
Click for larger Command UML Class diagram

8.3.2.3.  Participants
Table 8.16 describes the Command participants in the words of Gamma et al. [Gamma95, p.236-237] and lists the corresponding implementations developed in the evaluation.

Table 8.16PrintTable 8.16Command participants
ParticipantDescriptionImplementation
Command
  • Declares an interface for executing an operation.
Command<E>

SequenceCommand<E>
ConcreteCommand
  • Defines a binding between a Receiver object and an action.
  • Implements execute by invoking the corresponding operation(s) on Receiver.
CompositeCommand<E>, NextCommand<E>, ResetCommand<E>, ReverseCommand<E>, LogCommand<E>, NullCommand<E>
Client
  • Creates a ConcreteCommand object and sets its receiver.
Main
Invoker
  • Asks the command to carry out the request.
CommandProcessor
Receiver
  • Knows how to perform the operations associated with carrying out a request. Any class may serve as a Receiver.
Any Sequence<E> implementation

The implementation differs from the canonical implementation by Gamma et al. in that it uses a Command Processor to maintain, execute, and possibly undo commands, corresponding to a simple variant of the "POSA" Command Processor pattern [Buschmann96, p.277]. The processor thus functions as the Invoker participant, but it will be handed the commands to execute by the Client. Hence, the name and minimal description of the "pseudo—participant" as listed in table 8.17 below are not defined by Gamma et al., but by Buschmann et al. [Buschmann96, p.280].

Table 8.17PrintTable 8.17 — Additional Command entities
NameDescriptionImplementation
CommandProcessor
  • Activates Command execution, including commands spawned by an executed ConcreteCommand.
  • Maintains Command objects.
  • Provides additional services related to command execution.
CommandProcessor, CommandProcessingResult<E>

The execution of a Command<E> may spawn new commands to be executed immediately by the CommandProcessor in a depth—first manner. As far as we know, this is a novel approach to command execution. This lessen need for macro commands considerably and allow more control of command execution as composite (macro) commands no longer handle the execution of contained commands. Undo of spawned commands is possible in several different ways.

8.3.2.4.  Implementation
How intelligent should the command be? Design choice. Supporting undo and redo The SequenceCommand<E> class uses mementos for undo, while sub—classes also employ other means of undo as necessary. The CommandProcessor class implicit uses a history list in form of a collection of commands passed to it for execution. Avoiding error accumulation in the undo process By using the CommandProcessor class, the history list, undo, and error handling is made explicit and hence not up to the individual command. Error handling supports spawned commands. The implementation throws an exception in case undo fails, but different error handling strategies can be applied because of centralised control. Using C++ templates This corresponds to using generics with an upper bound as described in section 8.1.3.4.

8.3.3.  Interpreter
Interpreter is a Behavioural pattern with Class scope. The dk.rode.thesis.interpreter package contains the implementation.

8.3.3.1.  Intent
Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language [Gamma95, p.243].

8.3.3.2.  Structure
Figure 8.15 illustrates the Interpreter implementation as an UML Class diagram, where the pattern participants can also be identified. The pattern participants are described in the next section.

Figure 8.15PrintFigure 8.15Interpreter UML Class diagram
Click for larger Interpreter UML Class diagram

8.3.3.3.  Participants
Table 8.18 describes the Interpreter participants in the words of Gamma et al. [Gamma95, p.245-246] and lists the corresponding implementations developed in the evaluation.

Table 8.18PrintTable 8.18Interpreter participants
ParticipantDescriptionImplementation
AbstractExpression
  • Declares an abstract interpret operation that is common to all nodes in the abstract syntax tree.
Expression<E>

TypedExpression<E>, InitialisableExpression<E>
TerminalExpression
  • Implements an interpret operation associated with terminal symbols in the grammar.
  • An instance is required for every terminal symbol in a sentence.
TerminalExpression<E>, SequenceExpression<T,E>, CurrentExpression<E>, NextExpression<E>, ResetExpression<E>, ReverseExpression<E>, SetExpression<E>
NonTerminalExpression
  • One such class is required for every rule R ::= R1R2...Rn in the grammar.
  • Maintains instance variables of type AbstractExpression for each of the symbols R1 through Rn.
  • Implements an interpret operation for nonterminal symbols in the grammar. interpret typically calls itself recursively on the variables representing R1 through Rn.
NonTerminalExpression<E>, BinaryExpression<T,E>, AndExpression, AssignmentExpression<E>, BreakExpression<E>, CompareExpression<E>, ConditionalExpression<E>, ConstantExpression<E>, EqualExpression, FlowExpression<E>, NotExpression, OrExpression, VariableExpression<E>
Context
  • Contains information that is global to the interpreter.
Context
Client
  • Builds (or is given) an abstract syntax tree representing a particular sentence in the language that the grammar defines. The abstract syntax tree is assembled from instances of the NonTerminalExpression and TerminalExpression classes.
  • Invokes the interpret operation.
Main

Unlike Gamma et al., the implementation makes the interpreter explicit. This allows for much better error handling and transfer of evaluation control. Hence, the name and minimal description of the "pseudo—participant" as listed in table 8.19 below are not defined by Gamma et al.

Table 8.19PrintTable 8.19 — Additional Interpreter entities
NameDescriptionImplementation
Interpreter
  • Invokes the interpret operation on the Expression supplied by the Client.
  • Is used by the Client in favour of directly invoking interpret on Expression objects.
  • May provide additional services related to expression interpretation.
Interpreter<T>

8.3.3.4.  Implementation
Creating the abstract syntax tree Design choice. Here, the Builder implementation is used to build expressions that can be assembled. Defining the interpret operation The Expression<E> type defines the evaluate(Context) method, but the Interpreter<T> class defines the interpret method, performs error handling, and flow control. Sharing terminal symbols with the Flyweight pattern Design choice.

8.3.4.  Iterator
Iterator is a Behavioural pattern with Object scope. The dk.rode.thesis.iterator package contains the implementation.

8.3.4.1.  Intent
Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation [Gamma95, p.257].

8.3.4.2.  Structure
Figure 8.16 illustrates the Iterator implementation as an UML Class diagram, where the pattern participants can also be identified. The pattern participants are described in the next section.

Figure 8.16PrintFigure 8.16Iterator UML Class diagram
Click for larger Iterator UML Class diagram

8.3.4.3.  Participants
Table 8.20 describes the Iterator participants in the words of Gamma et al. [Gamma95, p.259] and lists the corresponding implementations developed in the evaluation.

Table 8.20PrintTable 8.20Iterator participants
ParticipantDescriptionImplementation
Iterator
  • Defines an interface for accessing and traversing elements.
java.util.Iterator<E> (external), ProcessableSequence<E>, ValueProcessor<E> (internal)
ConcreteIterator
  • Implements the Iterator interface.
  • Keeps track of the current position in the traversal of the aggregate.
SequenceIterator<E> (external), LoggingValueProcessor<E> (internal)
Aggregate
  • Defines an interface for creating an Iterator object.
java.lang.Iterable<T> (external)
ConcreteAggregate
  • Implements the Iterator creation interface (Aggregate) to return an instance of the proper ConcreteIterator.
IterableSequence<E> (external)

No classes have been defined to represent Aggregate and ConcreteAggregate for internal iterators. Java 6 defines no standard interface for internal iterators. The creation is left at the discretion of the context using internal iterators.

8.3.4.4.  Implementation
Java has built—in API and language support for the Iterator pattern. Who controls the iteration? The SequenceIterator<E> class represents an external iterator, while the ProcessableSequence<E> represents an internal iterator. Who defines the traversal algorithm? The composite.CompositeStrategy utilises package private access to ensure encapsulation and information hiding is not violated when the traversal algorithm is external. How robust is the iterator? Design choice. Standard iterators in Java are fail—fast, and fail immediately in case of concurrent modification. Additional Iterator operations Design choice. Using polymorphic iterators in C++ java.lang.Iterable<T> defines a factory method to return a java.util.Iterator<E> instance and the usage is illustrated in the IterableSequence<E> class. Iterators may have privileged access Illustrated in the meta.reflect.CallerClass.CallerIterator<C> inner class. Iterators for composites The composite.CompositeStrategy determines how the composite structure should be traversed. Null iterators Trivial, though note that making a null iterator for the meta.model.Sequence<E> type is not possible, because sequence semantics require that a sequence always have at least a single value.

8.3.5.  Mediator
Mediator is a Behavioural pattern with Object scope. This pattern is not implemented due to a lack of applicability because of its abstraction and granularity level. It offers no new information compared to the rest of the "Gang of Four" patterns with regards to language functionality.

8.3.5.1.  Intent
Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently [Gamma95, p.273].

8.3.5.2.  Participants
Table 8.21 describes the Mediator participants in the words of Gamma et al. [Gamma95, p.277].

Table 8.21PrintTable 8.21Mediator participants
ParticipantDescriptionImplementation
Mediator
  • Defines an interface for communicating with Colleague objects.
-
ConcreteMediator
  • Implements cooperative behaviour by coordinating Colleague objects.
  • Knows and maintains its colleagues.
-
Colleague Class
  • Each Colleague class knows its Mediator object.
  • Each Colleague communicates with its mediator whenever it would have otherwise communicated with another colleague.
-

8.3.5.3.  Implementation
As stated, the Mediator pattern has not been implemented because it only addresses general design issues. We still address the Implementation element, since it only discuss two items. Omitting the abstract Mediator class Design choice. Colleague/Mediator communication Design choice. Using the Observer pattern is possible as the means to communicate, for example the stand—alone observer.ObserverManager class that could handle different kind of colleagues without requiring them to implement a common super type. Passing itself as an argument is a common approach with trivial implementation. Also supported by ObserverManager.

8.3.6.  Memento
Memento is Behavioural with Object scope. The implementation is located in the dk.rode.thesis.memento package.

8.3.6.1.  Intent
Without violating encapsulation, capture and externalise an objects internal state so that the object can be restored to this state later [Gamma95, p.283].

Note that according to our separation of encapsulation and information hiding into two distinct concepts as described in section 2.1.1, encapsulation as described above refers to both concepts. Encapsulation must not be broken in the sense that the state must still be localised in the object, which must be hidden to shield it from unwanted access.

8.3.6.2.  Structure
Figure 8.17 illustrates the Memento implementation as an UML Class diagram, where the pattern participants can also be identified. The pattern participants are described in the next section.

Figure 8.17PrintFigure 8.17Memento UML Class diagram
Click for larger Memento UML Class diagram

8.3.6.3.  Participants
Table 8.22 describes the Memento participants in the words of Gamma et al. [Gamma95, p.285] and lists the corresponding implementations developed in the evaluation.

Table 8.22PrintTable 8.22Memento participants
ParticipantDescriptionImplementation
Memento
  • Stores internal state of the Originator object. The memento may store as much or as little of the originator's internal state as necessary at its originator's discretion.
  • Protects against access by objects other than the originator. Mementos have effectively two interfaces. Caretaker sees a narrow interface to the Memento - it can only pass the memento to other objects. Originator, in contrast, sees a wide interface, one that lets it access all the data necessary to restore itself to its previous state. Ideally, only the originator that produced the memento would be permitted to access the memento's internal state.
SequenceMemento<E>, GuardedSequenceMemento<E>
Originator
  • Creates a memento containing a snapshot of its current internal state.
  • Use the memento to restore its internal state.
Any MemorizableSequence<E> implementation like RangeSequence and MemorizableEnglishAlphabetSequence
Caretaker
  • Is responsible for the memento's safekeeping.
  • Never operates on or examines the contents of a memento.
Main

8.3.6.4.  Implementation
Language support Java does not support narrow (public) and wide (private) interface functionality because all methods declared in an interface must be public. A class may define and implement private methods, but they are inaccessible to other types. The GuardedSequenceMemento<E> class illustrates how private methods par design have to be made public for compile—time safety, but are guarded at runtime to ensure that only legal callers are allowed (see section 7.1.4.1). Storing incremental changes Design choice. The command.SequenceCommand<E> class use mementos to store the complete state.

8.3.7.  Observer
Observer is Behavioural with Object scope. The implementation is located in the dk.rode.thesis.observer package.

8.3.7.1.  Intent
Define a one—to—many dependency between objects so that when one object changes state, all dependants are notified and updated automatically [Gamma95, p.293].

8.3.7.2.  Structure
Figure 8.18 illustrates the Observer implementation as an UML Class diagram, where the pattern participants can also be identified. The pattern participants are described in the next section.

Figure 8.18PrintFigure 8.18Observer UML Class diagram
Click for larger Observer UML Class diagram

8.3.7.3.  Participants
Table 8.23 describes the Observer participants in the words of Gamma et al. [Gamma95, p.295] and lists the corresponding implementations developed in the evaluation.

Table 8.23PrintTable 8.23Observer participants
ParticipantDescriptionImplementation
Subject
  • Knows its observers. Any number of Observer objects may observe a subject.
  • Provides an interface for attaching and detaching Observer objects.
Observable<O>, AspectObservable<O,A>

ObservableSequence<O,A,E>, AspectObservableSequence<O,A,E>

SequenceObserversSequence<E,A>, AnnotatedObserversSequence<E>
Observer
  • Defines an updating interface for objects that should be notified of changes in a subject.
SequenceObserver<A> and any java.lang.Object annotated with the meta.reflect.@Executor annotation when used with ObserverManager or a sub—class of AnnotatedObserversSequence<E>
ConcreteSubject
  • Stores state of interest to ConcreteObserver objects.
  • Sends a notification to its observers when its state changes.
ObserverManager, DateSequence

SequenceObserversSequenceDecorator<E,A>, AnnotatedObserversSequenceDecorator<E>
ConcreteObserver
  • Maintains a reference to a ConcreteSubject object.
  • Stores state that should be consistent with the subject's.
  • Implements the Observer updating interface to keep its state consistent with the subject's.
CorrelatedSequenceObserver, PrintSequenceObserver, ProbeSequenceObserver, BirthdayRegistry

8.3.7.4.  Implementation
Mapping subjects to their observers java.util.Map<K,V> is used to store observers for fast lookup. Observing more than one subject The stand—alone ObserverManager class can manage and notify observers, typically used as a delegate by another object in a composition. It can store observers with different types, but using the same signature to notify the observers. Notification methods are specified via annotations, Its notifyObservers(java.lang.Object...) method uses varargs and it is up to the client to specify the signature of the actual observer notification methods and to supply the proper arguments. Hence, whether or not the subject is passed to the notification methods is a design choice based on the signature of the notification methods used. The AnnotatedObserversSequence<E> uses ObserverManager as a delegate and passes itself as the first argument to notifyObservers(Object...) when notification is performed. Observers can thus observe several subjects. Who triggers the update? Design choice. ObservableSequence<O,A,E> implementations issue notifications when their internal state changes, for example on invocation on next() and reset(). Dangling references to deleted subjects Subjects storing observers as hard references will prevent observers from being garbage collected. On the other hand, the ObserverManager class stores observers as weak references and if there are no other references to a given observer, it will be garbage collected. The manager handles this and purges weak references transparently when the observer has been garbage collected. Making sure Subject state is self—consistent before notification Template Method is used in the SequenceObserversSequence<E,A> and AnnotatedObserversSequence<E> classes to handle the notification in a state—consistent manner. Avoiding observer—specific update protocols The ObserverManager component can be used for both push and pull semantics because the type of observers and notification methods are configurable. Specifying modifications of interest explicitly Implementations of AspectObservable<O,A> allow observers of type O to subscribe to specific aspects of type A. Encapsulating complex update semantics The ObserverManager class can be considered a Change Manager. Sub—classes could simply override the notification method for different notification strategies.

8.3.8.  State
State is a Behavioural pattern with Object scope. The dk.rode.thesis.state package contains the implementation.

8.3.8.1.  Intent
Allow an object to alter its behaviour when its internal state changes. The object will appear to change its class [Gamma95, p.305].

8.3.8.2.  Structure
Figure 8.19 illustrates the State implementation as an UML Class diagram, where the pattern participants can also be identified. The pattern participants are described in the next section.

Figure 8.19PrintFigure 8.19State UML Class diagram
Click for larger State UML Class diagram

8.3.8.3.  Participants
Table 8.24 describes the State participants in the words of Gamma et al. [Gamma95, p.306-307] and lists the corresponding implementations developed in the evaluation.

Table 8.24PrintTable 8.24State participants
ParticipantDescriptionImplementation
Context
  • Defines the interface of interest to clients.
  • Maintains an instance of a ConcreteState sub—class that defines the current state.
StateableSequence<E>, AbstractStateableSequence<E>, ReversiblePrimeSequence

StepSequence
State
  • Defines an interface for encapsulating the behaviour associated with a particular state of the Context.
FunctionalState<E>

StepSequenceImpl
ConcreteState
  • Each sub—class implements a behaviour associated with a state of the Context.
Each constant in the ReversiblePrimeSequence.PrimeState enumeration

EvenSequence and OddSequence

8.3.8.4.  Implementation
Who defines the state transitions Design choice. Transitions are primarily handled by concrete states, such as the EvenSequence class and the ReversiblePrimeSequence.PrimeState constants, but also by ReversiblePrimeSequence when reversed. A table—based alternative Design choice. Trivial, not implemented. Creating and destroying State objects ReversiblePrimeSequence.PrimeState is an enumeration and all states are thus known beforehand. The states are stateless, small, and cheap to create. Creation (and destruction) is handled automatically and singleton behaviour for each state is guaranteed by the compiler. EvenSequence and OddSequence states are created on demand and garbage collected when no longer used. Using dynamic inheritance Not supported directly by Java, but dynamic proxies can simulate the behaviour. The invocation handler used by the proxy can change the target object of a given method (signature) to supply different implementations at runtime. This is illustrated in the StepSequence dynamic proxy that uses instances of the EvenSequence and OddSequence classes as two different implementations.

8.3.9.  Strategy
Strategy is a Behavioural pattern with Object scope. Also known as Policy. The implementation is located in the dk.rode.thesis.strategy package.

8.3.9.1.  Intent
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it [Gamma95, p.315].

8.3.9.2.  Structure
Figure 8.20 illustrates the Strategy implementation as an UML Class diagram, where the pattern participants can also be identified. The pattern participants are described in the next section.

Figure 8.20PrintFigure 8.20Strategy UML Class diagram
Click for larger Strategy UML Class diagram

8.3.9.3.  Participants
Table 8.25 describes the Strategy participants in the words of Gamma et al. [Gamma95, p.317] and lists the corresponding implementations developed in the evaluation.

Table 8.25PrintTable 8.25Strategy participants
ParticipantDescriptionImplementation
Strategy
  • Declares an interface common to all supported algorithms. Context uses this interface to call the algorithm defined by a ConcreteStrategy.
StringablePolicy<T>
ConcreteStrategy
  • Implements the algorithm using the Strategy interface.
Each SequencePolicy enumeration constant, and each ObjectPolicy enumeration constant
Context
  • Is configured with a ConcreteStrategy.
  • Maintains a reference to a Strategy object.
  • May define an interface that lets Strategy access its data.
Any Stringable<T> implementation, including sequences as they implement the interface

8.3.9.4.  Implementation
Defining the Strategy and Context interfaces Design choice. The Stringable<T> interface pass itself to a concrete strategy of type StringablePolicy<T>, i.e. delegation is used. Strategies as template parameters Generics with an upper bound can be used in place of templates, where the upper bound identifies the strategy functionality. This is analogous to the abstractfactory.PrototypicalRegistry functionality that uses an upper bound of StrictCopyable<?> to ensure that the actual type has a copy() method. Making Strategy objects optional Trivial design choice. This is illustrated in the Stringable<T> interface that will apply a default strategy if none is supplied.

8.3.10.  Template Method
Template Method is a Behavioural pattern with Class scope. The dk.rode.thesis.templatemethod package contains the implementation.

8.3.10.1.  Intent
Define the skeleton of an algorithm in an operation, deferring some steps to sub—classes. Template Method lets sub—classes redefine certain steps of an algorithm without changing the algorithm's structure [Gamma95, p.325].

8.3.10.2.  Structure
Figure 8.21 illustrates the Template Method implementation as an UML Class diagram, where the pattern participants can also be identified. The pattern participants are described in the next section.

Figure 8.21PrintFigure 8.21Template Method UML Class diagram
Click for larger Template Method UML Class diagram

8.3.10.3.  Participants
Table 8.26 describes the Template Method participants in the words of Gamma et al. [Gamma95, p.327] and lists the corresponding implementations developed in the evaluation.

Table 8.26PrintTable 8.26Template Method participants
ParticipantDescriptionImplementation
AbstractClass
  • Defines abstract primitive operations that concrete sub—classes define to implement the steps of an algorithm.
  • Implements a template method defining the skeleton of an algorithm. The template method class primitive operations as well as operations defined in AbstractClass or those of other objects.
SequenceTemplate<K,E>
ConcreteClass
  • Implements the primitive operations to carry out sub—class specific steps of the algorithm.
ZipSequence, FileSequence, NegativeSequence

8.3.10.4.  Implementation
Using C++ access control Primitive operations are declared as protected methods in the SequenceTemplate<K,E> abstract class. All sub—classes and classes in the same package can thus execute the primitive operations. Primitive operations that must be implemented are declared abstract, while the template methods have to be declared final to ensure that they cannot be overridden. Unlike C++, primitive operations overridden by a sub—class can be called in the (super) constructor. This is illustrated in SequenceTemplate<K,E>. A potential issue is that Java does not support multiple inheritance. Once the abstract class is inherited, no other classes can be inherited. An alternative is to use composition and interface implementation as illustrated in the builder.TypedExpressionBuilder<E> class; Builder often rely on sub—classing like Template Method. Minimizing primitive operations This is a design choice. Naming conventions Design choice. SequenceTemplate<K,E> utilises meaningful method name prefixes to identify primitive operations. Potentially important for reflective invocation.

8.3.11.  Visitor
Visitor is Behavioural with Object scope. The implementation is located in the dk.rode.thesis.visitor package.

8.3.11.1.  Intent
Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates [Gamma95, p.331].

8.3.11.2.  Structure
Figure 8.22 illustrates the Visitor implementation as an UML Class diagram, where the pattern participants can also be identified. The pattern participants are described in the next section.

Notice that there are two separate hierarchies of visitors: sequence value visitors and type visitors. The first performs visitation based on the type of values delivered by a sequence, while the latter performs visitation based on actual sequence type.

Figure 8.22PrintFigure 8.22Visitor UML Class diagram
Click for larger Visitor UML Class diagram

8.3.11.3.  Participants
Table 8.27 describes the Visitor participants in the words of Gamma et al. [Gamma95, p.334-335] and lists the corresponding implementations developed in the evaluation.

Table 8.27PrintTable 8.27Visitor participants
ParticipantDescriptionImplementation
Visitor
  • Declares a visit operation for each class for ConcreteElement in the object structure. The operation's name and signature identifies the class that sends the visit request to the visitor. That lets the visitor determine the concrete class of the element being visited. Then the visitor can access the element directly through its particular interface.
SequenceVisitor<P>

SequenceTypeVisitor<P>, SequenceValueVisitor<P>
ConcreteVisitor
  • Implements each operation declared by Visitor. Each operation implements a fragment of the algorithm defined for the corresponding class of object in the structure. ConcreteVisitor provides the context for the algorithm and stores its local state. The state often accumulates results during the traversal of the structure.
TypeVisitor, CountingVisitor, LoggingVisitor
Element
  • Defines an accept operation that takes a visitor as an argument.
TypeVisitableSequence<E>, ValueVisitableSequence<E>

AbstractVisitableSequence<E>
ConcreteElement
  • Implements an accept operation that takes a visitor as an argument.
VisitableCompositeSequence, VisitableLongSequence, VisitableRandomSequence, VisitableReversiblePrimeSequence

StringValuedVisitableSequence<E>, IntegerValuedVisitableSequence, DateValuedVisitableSequence, ReflectiveVisitableSequence<E>
ObjectStructure
  • Can enumerate its elements.
  • May provide a high—level interface to allow the visitor to visit its elements.
  • May either be a composite or a collection such as a list or a set.
SequenceTypeScanner, SequenceValueScanner

SimpleScanner

8.3.11.4.  Implementation
The SequenceValueVisitor<P> cannot overload visitation methods because it performs visitation based on type parameters that are erased at runtime. Double—dispatch As C++, Java only supports single—dispatch, so Visitor is indeed relevant. The ReflectiveVisitableSequence<E> is a decorator that uses reflection and naming conventions to avoid a static binding of the actual visitation method. Annotations can also be used as illustrated in observer.ObserverManager that uses annotations to determine notification methods. Who is responsible for traversing the object structure? The traversal of the composite sequence structure is made explicit by use of scanners, e.g. the SequenceTypeScanner and SequenceValueScanner types. Scanners represent the object structure, but only operate on it. Composite sequences do not traverse their children, this is handled by scanners as well. This allows scanners full control over the traversal strategy, for example depth—first or breath—first.

8.4.  Summary
Below, we list and summarise the key issues from the detailed evaluation of the "Gang of Four" patterns:

The pattern implementations illustrate that only Adapter with Class scope cannot be done in Java 6. All other pattern functionality can be implemented or simulated, but may require more work. Adapter with Class scope fails because the pattern abstraction and functionality, and not merely implementation issues, is targeted at languages supporting multiple inheritance. In this respect, we conclude that Adapter with Class scope is in fact a C++ idiom representing the general Adapter abstraction. Adapter with Object scope is easily implemented in Java 6, and dynamic proxies can furthermore be used to simulate multiple inheritance. However, in general, there are some problems related to the use of dynamic proxies that may influence pattern behaviour, such as testing for object equality.

The implementations express the themes and concepts described by Gamma et al. in a realistic manner, but also the "Best Practices" offered by Bloch concerning Java [Bloch01]. All implementation issues discussed by Gamma et al. in the Implementation and Sample Code elements in the "Gang of Four" patterns are expressed or simulated in the implementations.