• Ei tuloksia

Chapter 5 Theory of Task Generation

5.1 Role-Based Patterns

Our framework reuse interface specifications consist of role-based patterns. A pattern can be formally represented as a directed acyclic graph. The vertices of such a graph represent the pattern’s roles. The edges in a graph represent de-pendencies that model any kind of relationships between the entities that the

roles of the pattern describe. In the context of OO frameworks, dependencies are needed to model relationships like class inheritance, method overriding, and method invocations. In this thesis we use the following definition for directed graphs.

Definition 5.1: Directed Acyclic Graph Pair (V, D) is a directed acyclic graph, if the following conditions are satisfied:

V is a finite set of elements and

D is a relation in V, so that if v1, v2, …, vn are n elements in V such that v1Dv2

∧ … ∧ vn-1Dvn is true, then v1 ≠ vn.

For any directed acyclic graph (V, D) represented in this thesis, the elements of V are vertices and the relation D is a dependency relation. Consequently, in the rest of the thesis we talk about dependency graphs instead of directed acyclic graphs.

If v and w are vertices in V so that vDw, we say that v depends on w, there is a dependency from v to w, and that w is a dependency target of v.

5.1.1 An Example Pattern: Abstract Factory

Figure 5.1 shows an example of a role-based pattern represented as a dependency graph. The vertices of the graph represent roles and the edges represent depend-encies. For example, there is a dependency from role ac to role af. Roles also have cardinalities which together with dependencies restrict the number of target domain entities that can be playing the roles. The available cardinalities are: ex-actly one (“ ”), zero or one (“?”), at least one (“+”), and zero or more (“*”).

Figure 5.1: The Abstract Factory pattern represented as a dependency graph

As an example, Figure 5.1 actually represents the Abstract Factory design pattern (recall Chapter 2.4). Here we have modeled Abstract Factory with seven roles.

Role AbstractProduct represents the available abstract products. An abstract product declares an interface for the product objects of a particular type. Its car-dinality “+” means that there must be at least one class playing that role. Ab-stractFactory declares an interface for operations that create product objects.

Role abstractCreate models these operations, and the dependencies from ab-stractCreate to AbstractFactory and to AbstractProduct ensure that the abstract factory will contain a creation operation for each abstract product.

There can be a number of concrete factory classes that extend the abstract factory and implement the creation methods; role ConcreteFactory represents the

con-AbstractProduct + ConcreteProduct

AbstractFactory

ConcreteFactory Client

concreteCreate abstractCreate

cp cc af c

ap

cf ac

+

crete factories, and role concreteCreate models the concrete creation methods.

Role ConcreteProduct represents the concrete product classes that have to be in-stantiated by the corresponding concrete factory. A concrete product must im-plement the corresponding AbstractProduct interface. Finally, role Client stands for the class that utilizes the abstract factory.

When using the pattern as a part of a framework reuse interface specification, those pattern roles that describe the entities on the framework side, i.e. roles Ab-stractFactory, abstractCreate, AbstractProduct, and Client, are associated with the corresponding framework classes and methods during a phase that we call pattern initialization. After the initialization, an application developer can use the pattern for providing the missing application parts that will be playing the re-maining pattern roles: ConcreteFactory, concreteCreate, and ConcreteProduct.

(The process of associating pattern roles with program elements is supported by the task automaton which will be described in Chapter 5.2.)

The most interesting and most complex role of the pattern in Figure 5.1 is con-creteCreate. That role illustrates well how cardinalities are interpreted in role-based patterns. A role’s cardinality is always relative to the dependencies of the role. If a role has only one dependency we can say that the cardinality of the role determines the number of entities there must be playing the role for each entity playing the dependency target role. Role concreteCreate, however, has several dependencies that recursively lead to roles AbstractProduct and ConcreteFactory that have cardinalities allowing multiple target domain entities. How do we in-terpret the cardinality of concreteCreate? And, consequently, how do we end up creating the right concrete product in a concrete product creation method? This will be explained in detail in Chapter 5.2 where we describe how tasks are gener-ated from role-based patterns. However, before going into task details, let us see what other properties the pattern roles posses.

5.1.2 Scripts and Constraints

When applying the role-based patterns for a certain target domain, domain-dependent extensions to the pattern model have to be provided. These extensions include, for example, context-specific role types and constraints that can be at-tached to roles. (A target domain entity associated with a role must satisfy the constraints specified in the role.) In the domain of Java frameworks, for example, there would have to be role types for classes, operations, data fields, and other related program elements, as well as constraints for ensuring inheritance relation-ships between classes and method overriding relationrelation-ships between methods, for instance.

Besides constraints, a role is also associated with scripts that can be used for pro-viding dynamic user guidance when a target domain entity is needed for playing the role. Scripts can also be used for purposes like code generation in suitable domains. Scripts differ from constraints in that the validity of a role’s constraints is constantly checked after the role has been bound to a target domain entity, whereas scripts are only used for generating new textual elements; scripts are not

compared against the elements afterwards. (This policy of separating constraint checking and code generation was discussed earlier in Chapter 4.3.3.)

The scripts and constraints of a role typically refer to the properties of the target domain entities that are playing the dependency target roles. For example, each time a task is generated from a role, the taskDescription script of the role is evaluated and shown to the user. Role concreteCreate in Figure 5.1 might have, for example, the following task description script: Provide concrete creation method in class ‘<#:/cf.i.shortName>‘. The method must override method ‘<#:/

ac.i.signature>‘ and return an instance of class ‘<#:/cp.i.shortName>‘. Expres-sions /cf.i, /ac.i, and /cp.i refer to the Java entities bound to the corresponding roles. The expressions shortName and signature are calls to predefined system functions that return the unqualified name and the signature of the entities, re-spectively. At a certain time the script might evaluate, for example, to Provide concrete creation method in class ‘XPFactory’. The method must override method ‘createButton’ and return an instance of class ‘XPButton’. Similarly, the default template script of role concreteCreate could contain a proper template for generating the required code. Furthermore, concreteCreate would probably be associated with at least method overriding and class instantiation constraints that would afterwards ensure that the method remains valid.

Note that there are actually different kinds of script interpreters for the scripts.

The small examples above assume that simple template interpreter is used.

Scripts, constraints, and script interpreters are discussed in more detail in Chapter 6.1 where we introduce the Java-framework-specific patterns.