The Separation construction principle places the template and hook methods in separate classes. Let's rework the CurrencyConverter example. The hook method round() becomes part of another class RoundingPolicy. The class with the hook method is, in most cases, an abstract class or a Java interface (see below). An instance of CurrencyConverter refers to an object of static type RoundingPolicy through an instance variable rPol. Further, CurrencyConverter typically offers a method that allows a change of the rounding policy. This is method defineRoundingPolicy() in Example 4.4. In other words, the behavior of the template method convert() in CurrencyConverter can be changed by plugging in any specific RoundingPolicy object through the method defineRoundingPolicy().
public class CurrencyConverter { RoundingPolicy rPol= new DefaultRPol(); public void convert(...) { double result, value; switch (...) { case ... : result= rPol.round(value); break; case ... : result= rPol.round(value); break; } } public void defineRoundingPolicy (RoundingPolicy rp) { rPol= rp; } } public abstract class RoundingPolicy { public abstract double round(double val); } public class DefaultRPol extends RoundingPolicy { public double round(double val) { ... // do a four digit after comma rounding of // 'val' } }
The main difference between the Unification and Separation construction principles is the ease of extension. The Unification principle requires overriding of the hook method in a subclass in order to change the behavior of the template method. Common object-oriented languages and their runtime systems do not allow such modifications at runtime. The Separation principle on the other hand only requires an object instantiation and a redefinition of a reference that can be done at runtime. Any of the subclasses of RoundingPolicy might be instantiated and plugged into the CurrencyConverter object. Runtime adaptations of the template method behavior are easier with the Separation principle.
Figure 4.12 shows three subclasses of RoundingPolicy. The connecting line between classes CurrencyConverter and RoundingPolicy expresses an association that corresponds to the instance variable rPol in class CurrencyConverter. The italic style of the class name RoundingPolicy and method round() marks the class and its method as abstract this is standard UML. The rounding behavior of the three subclasses DefaultRPol, RPol10, and RPol100 is as follows: DefaultRPol rounds a number to four digits after the comma; RPol10 rounds the decimal place; RPol100 rounds by the hundreds. Plugging in one of these rounding policies in a currency converter object adapts the rounding behavior of the template method convert() accordingly. Example 4.5 presents a source code fragment that accomplishes these adaptations, assuming that the end-user chooses a rounding policy via a menu.
// the local variable 'currConv' refers // to an instance // of class CurrencyConverter switch (...) { // menu selection case ... : currConv.defineRoundingPolicy(new DefaultRPol()); break; case ... : currConv.defineRoundingPolicy(new RPol10()); break; case ... : currConv.defineRoundingPolicy(new RPol100()); break; }
Figure 4.13 shows the UML diagram of three subclasses of RoundingPolicy and an additional subclass SpecialRPol. SpecialRPol rounds the decimal place for numbers up to 50,000 and rounds by the hundreds for larger numbers. SpecialRPol should be defined, implemented and plugged into a CurrencyConverter instance while the application that uses currency conversion is running. In other words, the class SpecialRPol was not available when that application was started. While the application is running, the additional class SpecialRPol is implemented and an instance of it should be used to adapt a CurrencyConverter object.
This requires the dynamic loading and linking of a class to a running application, and the instantiation of a class whose name is not known in advance. A simple user interface for plugging in additional rounding policies could be a dialog box that allows entering the class name as string (see Figure 4.14).
Java offers the dynamic loading and linking of classes and its meta-information system suffices for instantiating classes whose names are specified as strings. Example 4.6 shows the source code fragment that instantiates a rounding policy based on the class name that is provided as string. The static forName() method of class Class searches for the Java byte code in certain directories, if the class is not yet linked to the running application. Method forName() then generates an instance of class Class whose method newInstance() creates an instance of the class that was specified as string.
public RoundingPolicy createRoundingPolicy(String className) { Object aRPol; try { Class aClass= Class.forName(className); aRPol= aClass.newInstance(); } catch (Exception e) { return null; } if (aRPol instanceof RoundingPolicy) return (RoundingPolicy)aRPol; else return null; } // String clName= read the text entered in the dialog entry field (see Fig. 4.14) RoundingPolicy rPol= createRoundingPolicy(clName); if (rPol != null) currConv.defineRoundingPolicy(rPol);
In the Separation construction principle, the template and the hook methods are defined in separate classes coupled via an association. The class containing h() could also be a Java interface. Figure 4.15 illustrates this combination of template and hooks. As a consequence, UML-F provides a means of marking these items (T, H, t(), and h()), and in particular whether class H is supposed to be extended in the framework adaptation process.
Analogous with the Unification construction principle, the static structure of the Separation construction principle determines the corresponding UML-F tags. Figure 4.16 shows the static structure of the Separation construction pattern annotated with the UML-F tags for that construction pattern. As can be seen, there are four concepts that need to be documented using UML-F tags.
Separation T marks the class that contains the template method
Separation H marks the class that contains the hook method
Separation t marks the template method
Separation h marks the hook method.
As a short cut we suggest using the tags Sep T , Sep H , Sep t , and Sep h .
Figure 4.17 applies the Separation tags to annotate the conversion design aspect of classes Account and CurrencyConverter. Though again not mandatory, we chose the name Conversion for that application of the Separation construction principle and explicitly group the tags.
The mapping between a construction principle and classes or methods of a framework allows adequate documentation, and as a consequence, better understanding of a framework. Figure 4.18 shows this mapping schematically. The use of tags makes for a more compact textual mapping which is equally informative. Beyond a simple marking of the templates and hooks, the Separation and Unification UML-F tags express semantic information which is inherent in these construction principles.
Analogous with the proposed tool support for template and hook tags, Figure 4.19 exemplifies how a tool could exploit UML-F tags for framework construction principles. If a user would like to view instances where the Separation construction principle was applied, they select one incarnation from a pop-up menu by name. Based on the selection, the tool might establish hyperlinks that link the corresponding classes/interfaces and methods. Figure 4.19 illustrates this linkage. As an alternative view, the structural components of the construction principle might be highlighted, for example by labels and lines with arrow heads as in Figure 4.18.
Table 4.4 summarizes the UML-F Separation tags. The table documents all four tags that form a logical unit.
Tags | Separation T , Separation H , Separation t , Separation h ; or their abbreviations Sep T , Sep H , Sep t , Sep h . |
Apply to | class ( Separation T , Separation H ), method ( Separation t , Separation h ), interface ( Separation H ); see Figure 4.16. |
Type | String. |
Motivation and purpose | The tags highlight the application of the Separation construction principle in a framework bymarking the corresponding template and hook methods as well as the classes/interfaces to which the particular methods belong to. |
Informal explanation of effect | The Separation construction principle implies that the adaptation of the framework variationpoint can be accomplished by plugging in specific instances of classes of static type Separation-H . If the framework does not provide appropriate classes of static type Separation-H , additional ones have to be defined by overriding of the hook method(s). |
Expansion | The Separation tags Separation t and Separation h expand to the more basic UML-F template and hook tags (see Figure 4.18). |
Discussion | The four UML-F Separation tags must always be used together. Several instances of the Separation h tags are possible, if the Separation t method calls them. |