• Ei tuloksia

The Library of Reusable Patterns

Chapter 6 Applying Task Generation to Framework Specialization

6.2 Specifying Framework Reuse Interfaces

6.2.1 The Library of Reusable Patterns

The methodology for creating framework reuse interface specifications relies on a library of generic patterns. These patterns can be used as a basis for framework-dependent patterns that constitute a framework reuse interface specification.

Figure 6.16 shows the structure of the generic pattern library. The library con-tains five top-level architectures: Generic OO Patterns, Bean Patterns, Metapat-terns, Design PatMetapat-terns, and Text File Patterns. Note that we represent architec-tures as UML packages with stereotype «architecture».

Figure 6.16: The structure of the generic pattern library

«architecture»

Generic OO Patterns

«architecture»

Bean Patterns

«architecture»

Text File Patterns

«architecture»

Metapatterns

«architecture»

Design Patterns

The patterns in Generic OO Pattern architecture include patterns for representing the basic OO constructs and the relationships between them. Bean Patterns archi-tecture contains patterns for describing JavaBeans and, in practice, they offer support for implementing class properties quickly. The dependency relationship from the Bean Patterns architecture to the Generic OO Patterns architecture de-notes that the patterns in Bean Patterns extend the patterns in Generic OO Pat-terns. The Metapatterns architecture implements Pree’s metapatterns (recall Chapter 2.5) by extending patterns in the Bean Patterns and Generic OO Pat-terns architectures. Design PatPat-terns contains implementations for some of the structural patterns by Gamma et al. (recall Chapter 2.4). Design Patterns uses all the aforementioned patterns in its implementation. Finally, Text File Patterns contains reusable patterns that can be utilized for modeling text files.

The patterns in Generic OO Patterns and Bean Patterns architectures describe such fundamental OO features that they are in practice applicable and useful when creating a reuse interface specification for any OO framework. The patterns in Metapatterns and Design Patterns architectures are a bit more specialized. By definition metapatterns describe the flexibility aspects in OO programs. Conse-quently, metapatterns can be used in almost any case where flexibility is a con-cern. By using them one can document the templates and hooks in the frame-work. However, from a pattern-reuse point of view, metapatterns offer hardly more support than Generic OO Patterns and Bean Patterns already do. The same goes partly for the patterns in Design Patterns architecture: for the sake of docu-menting the existence of design patterns in the framework whose reuse interface we are modeling, the Design Patterns architecture is truly applicable. However, since the design patterns may have dozens of different variations, the variations provided by the patterns in the Design Patterns architecture may not be sufficient and they thus must be further specialized. In this thesis the patterns in Metapat-terns and Design Pattern architectures are considered mainly as another case study of specializing the patterns in Generic OO Patterns and Beans Patterns architectures for different purposes. Appendix B contains the full versions of the patterns.

Figure 6.17 shows the relationships between the patterns in the Generic OO Pat-terns architecture. The Core architecture inside Generic OO PatPat-terns contains patterns Class, Method, Constructor, and Field. These represent properties for basic OO elements. The patterns directly inside the Generic OO Patterns archi-tecture — Inheritance, ClassInstantiation, MethodOverriding, MethodCall, and FieldReference — represent possible interactions between the basic OO ele-ments.

Code example 6.18 shows the specification for the Class pattern. The pattern de-clares one role Class that has cardinality “*”. The scripts of the role have default values that are generally applicable in any class role. Thus, any class role in any pattern that extends the Class role from the Class pattern can utilize these default values. For instance, the taskTitle and taskDescription scripts (lines 3 – 7) define simple templates for representing information about a task generated from the role. Note that if the Class pattern is extended in another pattern, these scripts are

evaluated in the context of the extending pattern. That is why the tag <#:role-Name> in line 3, for example, evaluates to the name of the extending role.

Figure 6.17: The Generic OO Patterns architecture

Code example 6.18: The specification for the Class pattern

1

<role name=“Class” type=“class” cardinality=“*”>

<script name=“taskTitle”>Provide class ‘<#:roleName>‘</script>

<script name=“taskDescription”>

Provide a Java type for the role ‘<#:roleName>‘.

<br><br><small>(Pattern note: the class will play the role ‘<#:longRoleName>‘.)</small>

</script>

<script name=“defaultTemplate”>

/**

* This class was generated by JavaFrames.

* JavaFrames (note): This class plays the role ‘<#:longRoleName>‘.

*/

public class <#:roleName><#:taskNumber> { } </script>

<script name=“defaultInterfaceTemplate”>

/**

* This interface was generated by JavaFrames.

* JavaFrames (note): This interface plays the role ‘<#:longRoleName>‘.

*/

public interface <#:roleName><#:taskNumber> { } </script>

<script name=“insertLocationTag”>codeInsertionPoint</script>

<script name=“defaultSpecializationName” interpreter=“SXI”>roleName</script>

<script name=“description”>

<p>The role ‘<#:longRoleName>‘ was bound to ‘<#:/Class.i.longName>‘. </p>

<p><small>

Class Method «pattern»

«pattern»

31

The defaultTemplate script (lines 8 – 14 in Code example 6.18) defines a basic template for generating code for a class. The template contains a formal Java comment that names the pattern role which the code was generated from and the actual class declaration that sets the role name as the name for the generated class. The tag <#:taskNumber> evaluates to the current number of tasks gener-ated from the role in the current pattern. The defaultInterfaceTemplate script (lines 15 – 21) defines a default template for an interface. The insertLocationTag script (line 22) defines a tag name that can be used in target source code to define the place where code is generated. Script defaultSpecializationName (line 23) defines a default name for a role extended from this role by task postponement.

The description script (lines 24 – 32) is shown in the JavaFrames UI for bound roles; by default the script contains information about how the task made from the role was executed. The script is also utilized by a tool called JavaPages [Savolainen 2003; Hakala et al. 2002] that can be used to record JavaFrames ses-sions to illustrate the framework specialization process.

The other patterns inside the Core architecture — Method, Constructor, and Field — all utilize (i.e. extend) the Class pattern to represent the declaring class for a method, a constructor, or a field, respectively. As an example, Code exam-ple 6.19 shows the Method pattern.

Code example 6.19: The specification for the Method pattern

1

<generalization name=“class” target=“[Generic OO Patterns/Core]Class”/>

<role name=“Class” type=“class” cardinality=“*”>

<generalization name=“class” target=“/Class”/>

<role name=“method” type=“method” cardinality=“*”>

<script name=“taskTitle”>Provide method ‘<#:roleName>()’</script>

<script name=“taskDescription”>

Provide a Java method for the role ‘<#:roleName>‘.

<br><br> <small>(Pattern note: the method will play the role ‘<#:longRoleName>‘.)</small>

</script>

<script name=“defaultTemplate”>

/**

* This method was generated by JavaFrames.

* JavaFrames (note): This method plays the role ‘<#:longRoleName>‘.

*/

public void <#:roleName><#:taskNumber>() { <#:fillInBodyTemplate>}

</script>

<script name=“insertLocationTag”>codeInsertionPoint</script>

<script name=“description”>

<p>The role ‘<#:longRoleName>‘ was bound to ‘<#:/Class/method.i.longName>‘.</p>

<p><small><b>Task title was: </b><i><#:taskTitle></i><br><br><b>Task description was: </b>

<i><#:taskDescription></i></small></p>

</script>

<script name=“defaultSpecializationName” interpreter=“SXI”>roleName</script>

<script name=“fillInBodyTemplate”>// JavaFrames (task): Implement method body.</script>

</role>

</role>

</pattern>

As the generalization clause on row 2 suggests, the Method pattern extends the Class pattern from the Core architecture (also recall Figure 6.17). The only top-level role of the Method pattern, i.e. role Class (line 3), extends role Class from the generalization pattern. The Class role in Method has no scripts since they are inherited from the generalization. Note that this shortens the pattern specification significantly and, furthermore, if we make changes in the generalization role Class, the changes are immediately visible in the specialization roles. For exam-ple, we might want to change the (default) taskDescription for all class roles so that instead of saying “…a Java type…” we want to say “…a Java class or inter-face…”. If all the class roles inherit role Class from pattern Class, we only need to make the change in one place.

In Code example 6.19, there is a child role called method inside the Class role (lines 5 – 26). The method role declares proper general scripts applicable for any method. Scripts taskTitle, taskDescription, description, defaultTemplate, insert-LocationTag, and defaultSpecializationName are utilized in user documentation, user guidance, code generation, and task postponement (as discussed earlier).

Script fillInBodyTemplate is referred from the defaultTemplate. The contents of fillInBodyTemplate are not directly embedded in defaultTemplate, because we want to be able to refer that particular piece of template also from other scripts.

For example, a role specialization might override defaultTemplate, but still use the definition for fillInBodyTemplate from the super role.

Code example 6.20 shows a situation where a pattern (ClassInstantiation) com-bines the properties of two other patterns (Class and Method) by inheriting them (also recall Figure 6.17). ClassInstantiation has two class roles. Class (lines 4 – 7) represents a class that gets instantiated and Instantiator (lines 8 – 38) specifies a class that instantiates the other class. Role Class overrides the script taskTitle inherited from pattern Class with a more context-specific version (line 6). All the other scripts are inherited as-is.

Code example 6.20: The specification for the ClassInstantiation pattern

1

<generalization name=“class” target=“[Generic OO Patterns/Core]Class”/>

<generalization name=“method” target=“[Generic OO Patterns/Core]Method”/>

<role name=“Class” type=“class” cardinality=“*”>

<generalization name=“class” target=“/Class”/>

<script name=“taskTitle” interpreter=“-”>Provide a class to instantiate</script>

</role>

<role name=“Instantiator” type=“class” cardinality=“*”>

<generalization name=“method” target=“/Class”/>

<script name=“taskTitle”>Provide a class that instantiates another class</script>

<script name=“taskDescription”>Provide a class that instantiates another class (Pattern note: the class will play the role ‘<#:longRoleName>‘.)</script>

<role name=“method” type=“method” cardinality=“*”>

<generalization name=“method” target=“/Class/method”/>

<script name=“taskTitle”>Provide a method that instantiates a class</script>

<script name=“taskDescription”>Provide a method that instantiates a class.<br>

(Pattern note: the method will play the role ‘<#:longRoleName>‘.)</script>

<constraint type=“class instantiation”>

<dependency target=“/Class”/>

<script name=“value” interpreter=“SXI”>/Class.i</script>

21

<script name=“taskTitle”><#:defaultTaskTitle></script>

<script name=“taskDescription”><#:defaultTaskDescription></script>

<script name=“defaultSpecializationName” interpreter=“SXI”>roleName</script>

</constraint>

<role name=“ClassInstantiationCode” type=“code fragment” cardinality=“?”>

<dependency target=“/Class”/>

<script name=“insertLocationTag”>codeInsertionPoint</script>

<script name=“defaultTemplate”>

<#:/Class.i.longName> <#:/Class.i.shortName.javaMemberName> = new <#:/Class.i.longName>();

</script>

<script name=“taskTitle”>Provide ‘<#:roleName>‘</script>

<script name=“defaultSpecializationName” interpreter=“SXI”>roleName</script>

<script name=“taskDescription”>Provide a Java code fragment for the role ‘<#:roleName>‘.

</script>

</role>

</role>

</role>

</pattern>

Role Instantiator (lines 8 – 38 in Code example 6.20) overrides the taskTitle and taskDescription scripts from pattern Method with the versions on lines 10 – 12.

Role method inside Instantiator represents a method that instantiates a class bound to role Class. It overrides the default scripts for taskTitle and taskDescrip-tion (lines 15 – 17). In additaskDescrip-tion, it contains a class instantiataskDescrip-tion constraint (lines 18 – 24) and a code fragment role (lines 25 – 36). The constraint places restric-tions on the methods bound to role method and the code fragment role offers support for generating new code to fix a possible constraint violation. Note that we can utilize the automatic constraint fixing mechanism too, but for an uncer-tain constraint it is also useful to declare a code fragment role since it can be overridden in extending patterns to produce more precise results. For certain constraints the automatic constraint fixing produces such good results that a code fragment role is not needed. Using constraints together with code fragments like this is a recurring convention in JavaFrames patterns.

The class instantiation constraint has a dependency to role Class (line 19). This means that the constraint gets evaluated for each program element bound to role Class. (Recall the discussion on constraints in Chapter 6.1.2.) The value script of the constraint is /Class.i and it thus evaluates to the specific class that is bound to the Class role and for which the particular constraint task is generated. Conse-quently, the constraint ensures that a method bound to role method must instanti-ate all the classes bound to role Class. (See the semantics of the class instantia-tion constraint in Appendix A.) Similarly as the class instantiainstantia-tion constraint, the ClassInstantiationCode code fragment has a dependency to role Class. However, the cardinality of ClassInstantiationCode is “?”, so the tasks generated from the role are optional: the developer using the pattern may use the tasks to fix con-straint violations if there are some.

The other patterns inside Generic OO Patterns, i.e. Inheritance, MethodOverrid-ing, MethodCall, and FieldReference, describe relationships between basic OO elements in a similar manner as ClassInstantiation does. Inheritance specifies an inheritance relationship between classes or interfaces. MethodOverriding and MethodCall specify an overriding and a method call relationship between two

methods, respectively. FieldReference describes methods that refer a field. All these patterns declare a corresponding constraint to monitor the existence of the relationship. Those constraints that are uncertain have a code fragment role for generating code that fixes possible constraint violations.

Figure 6.21 shows the contents of the Bean Patterns architecture. The Bean-Locate pattern specializes the Method pattern from the Generic OO Patterns ar-chitecture to describe getter methods. The Bean-Skeleton pattern, in turn, special-izes Bean-Locate and implements a core for a pattern that describes property get-ters, property setget-ters, and property change support. The Bean-IndexedProperties-Skeleton pattern specializes Bean-Bean-IndexedProperties-Skeleton and adds support for inserting and removing items to and from an indexed property. The patterns in the Skeleton architecture have three concrete specializations. Bean-SimpleProperties special-izes Bean-Skeleton and defines the contents for the getter and setter, for instance, so that they describe a simple bean property. Bean-IndexedArrayProperties and Bean-IndexedVectorProperties specialize Bean-IndexedProperties-Skeleton into array-based and vector-based implementations, respectively.

The bean patterns described above offer an important supplement to the palette of generic OO patterns. The bean patterns essentially describe a protocol for im-plementing class properties. Most OO languages, including Java, do not offer the property mechanism as a built-in feature even though it is a central feature when encapsulating individual class attributes.

Figure 6.21: The Bean Patterns architecture

Figure 6.22 shows the structure of the Metapatterns architecture (recall the dis-cussion on metapattern in Chapter 2.5). The top level of this architecture is im-plemented so that patterns 1:1 Connection, Unification, and 1:N Connection

ex-«pattern»

Bean-Skeleton

«architecture»

Bean Patterns

«architecture»

Skeleton

Method Method

FieldReference, Class, Method, Field

«pattern»

Bean-IndexedProperties-Skeleton

«pattern»

Bean-Locate

«pattern»

Bean-SimpleProperties

«pattern»

Bean-IndexedArrayProperties

«pattern»

Bean-IndexedVectorProperties

tend patterns from Generic OO Patterns and Bean Patterns. Patterns 1:1 Recur-sive Connection, 1:1 RecurRecur-sive Unification, 1:N RecurRecur-sive Connection, and 1:N Recursive Unification form the second layer in the MetaPatterns architecture.

The former two are implemented by extending the 1:1 Connection pattern, whereas the latter two are based on the 1:N Connection pattern. In addition, pat-terns 1:1 Recursive Connection and 1:N Recursive Unification extend Meth-odOverriding from the Generic OO Patterns architecture. On the whole, all the constraints and most of the scripts in the Metapatterns architecture are inherited from Generic OO Patterns. (Recall from Chapter 2.5 that metapatterns actually just represent combinations of using the basic OO mechanisms.)

Figure 6.22: The Metapatterns architecture

Figure 6.23 includes the Design Patterns architecture with Observer, Singleton, AbstractFactory, and Composite patterns. All of these patterns make heavy use of other patterns in other architectures. For example, the AbstractFactory pattern is implemented by extending (1) patterns Method and Constructor from the Core architecture, (2) patterns ClassInstantiation, MethodOverriding, and Inheritance from the Generic OO Patterns architecture, (3) pattern Bean-IndexedVector-Properties from the Bean Patterns architecture, and (4) pattern 1:1 Connection from the Metapatterns architecture.

Note that a JavaFrames pattern does not necessarily describe Java entities. In or-der to be able to model various configuration files in frameworks, we have pro-vided JavaFrames patterns with roles for text files, text fragments, and arbitrary names. The NamedTextFragmentInFile pattern in the Text File Patterns architec-ture (see Figure 6.24) caparchitec-tures a frequently occurring situation where a snippet of text in a file has a name that is supposed to be referred from elsewhere.

«architecture»

Metapatterns

«pattern»

1:1 Connection

«pattern»

1:1 Recursive Connection

«pattern»

1:1 Recursive Unification

«pattern»

Unification

«pattern»

1:N Connection

«pattern»

1:N Recursive Connection

«pattern»

1:N Recursive Unification

Bean- SimpleProperties MethodOverriding MethodCall Bean-Indexed ArrayProperties

Note that the library of reusable patterns is by no means complete. On the con-trary, it can be supplemented with new reusable patterns that describe, for exam-ple, coding conventions or templates for quick code generation of frequently re-curring code structures.

Figure 6.23: The Design Patterns architecture

Figure 6.24: The Text File Patterns architecture

The generic OO patterns and bean patterns described in this chapter provide a backbone in the methodology of creating patterns for a framework reuse interface specification. Before showing how the generic patterns have been used when specifying the reuse interface of JHotDraw, we discuss different types of patterns in framework reuse interface specifications and the problem of identifying framework hot spots (recall Chapter 3.3) in the first place.