The UML-F tags in the above sections are useful for any object-oriented model including frameworks. This section
Many frameworks come together with prefabricated application classes that do not belong to the framework itself. These additional classes can be studied in order to understand the standard usage of a framework by examining and adapting their code, whereas the framework classes
The tag «application» marks application-specific classes. During the framework adaptation process, this tag can mark newly introduced classes as well. The «framework» tag marks classes and interfaces that are
|
Tag
|
Applies to | Value type | Description |
|---|---|---|---|
| «application» | Class, package, interface | Boolean | The class/interface/package does not belong to a framework, but to an application. |
| «framework» | Class, package, interface | Boolean | The class/interface/package belongs to the framework. |
| «utility» | Class, package, interface | Boolean | The class/interface/package belongs to a utility library or the runtime system. |
One important principle of framework design underlies the tags presented in this section: no framework element should be directly changed for an application by modifying its source code. This principle is necessary to enable reuse of the framework in different applications. However, this principle can only be applied if the framework is mature because the development of a framework usually goes hand-in-hand with the development of a number of applications.
In framework, classes,
| Tag name | Applies to | Value type | Description |
|---|---|---|---|
| «fixed» | Class, method, generalization | Boolean | The element is fixed. Methods may not be changed in subclasses. In a generalization relation new subclasses may not be added. |
| «adapt-static» | Interface, class, method, generalization | Boolean | The element can be adapted during design time through subclassing or interface implementation. A method can be overridden in subclasses. New methods and attributes may be added to the subclass and existing methods may be overridden. During runtime the element is fixed. |
| «adapt-dyn» | Interface, class, method, generalization | Boolean | The interface, class, method can be changed at runtime, for example through dynamic loading of new subclasses. Additional subclasses typically override methods. |
Dynamic adaptations during runtime typically require extra effort and need special treatment. Not every implementation language and/or runtime system allows a dynamic adaptation. Java supports it through its
This section discusses the above tags in the context of methods. Methods can be «fixed», or can have the «adapt-static» or «adapt-dyn» tag attached to them.
For many methods a framework already provides an implementation. To keep that implementation flexible, the method can be adapted through hook methods
[12]
that are called in the methods body. A method can be explicitly tagged as a «fixed» method if it should not be changed through adaptation or overriding,
[12] Hook methods are described in detail in Chapter 4.
If the framework itself provides more than one possible implementation of a «fixed» method in several subclasses, the application developer may choose (or the framework chooses) which implementation to use, but adding new
The «adapt-static» tag allows the application developer to modify a method in a new subclass during design time (see Figure 3.14). This tag marks methods in framework classes that are intended to serve as a connection to the application. Such methods are often abstract, or provide only a
[13] As the «adapt-static» tag corresponds to overriding of methods in subclasses, we were tempted to introduce the tag «override» instead, but for the sake of
uniformity we chose «adapt-static».
On rare occasions implementations vary widely, even during system operation. Then it might not be possible to think of all cases up front. Instead, system designers offer a dynamic adaptation of the system. If the framework is designed in such a way that new implementations of a method can be added during runtime then that method can be marked as «adapt-dyn». Such a dynamic implementation occurs, for example, if the system should run without shutdowns.
Dealing with this form of adaptation is straightforward in languages such as Smalltalk and the Common Lisp Object System (CLOS). Java also provides the flexibility through its meta-information system and the dynamic loading of classes. A class that
Dynamic loading of methods introduces some overhead. So one should check
Methods in framework classes are marked as «fixed» by default - this adds a security level to framework use. If the framework developer wants to allow adaptation, this has to be enabled explicitly. Furthermore, in larger frameworks it appears that more than the half of the methods are
A framework may have a large number of methods tagged as «adapt-static» or «adapt-dyn». It is not a requirement to actually adapt all these methods. Instead, the decisions as to which methods to adapt depend on the application being developed. The
From a conceptual viewpoint, classes and interfaces share the characteristic that they define a type through their signature. During system modeling, classes and interfaces can be treated in a rather uniform way. Nevertheless, it makes no sense to apply the «fixed» tag to interfaces because an interface cannot provide a method implementation and any class implementing the interface therefore must be allowed to define its own method implementation.
If none of the methods that a class provides are to be redefined in an application, then the class can be marked with the «fixed» tag (see Figure 3.15). The «fixed» tag for classes thus serves as a shortcut for attaching the tag to each of its methods. This allows the addition of new subclasses and the definition of new methods within these subclasses, but it does not allow the modification of any inherited methods. Although a class may be «fixed», the framework may provide a number of subclasses where methods are already overridden.
As a consequence of being a shortcut for attaching the tag to all methods, the «fixed» tag for classes does not carry over to its subclasses directly. Instead, it propagates only to all methods that are inherited (see Figure 3.16). The practical use of this tag is restricted to
Marking a class or an interface with the «adapt-static» tag acts as a shortcut for marking all its methods as «adapt-static».
[14]
In this case, all its methods can be adapted by the application developer through adding (sub)classes. However, the «adapt-static» tag can be overruled for individual methods in the class. If a method is tagged as «fixed», then this method may not be adapted - explicit method tags overrule class tags when they are more
[14] For the sake of readability, the following presentation mostly refers to 'classes' synonymously to 'interfaces', although a replacement of the terms 'class' by 'interface' would imply further changes of the description. For example, classes implement interface methods instead of overriding them.
Finally, the «adapt-dyn» tag can be attached to a class or an interface to express the possibility of dynamic adaptation during runtime. The use of «adapt-dyn» for a class/interface reveals the existence of at least one method with the same tag, thus encouraging dynamic loading of classes during runtime. The tag «adapt-dyn» applied to a class promotes to all its methods that are not marked differently. In practice, however, such classes often have only one or a few methods intended for dynamic adaptation.
So far, the focus has been on tags for methods, classes and interfaces; and the tags for classes and interfaces as shortcuts for method tags. All these tags specify the degree of adaptability of particular methods in classes or interfaces. Thus, during a framework adaptation it is of interest to understand where classes should be added. For this purpose, UML-F provides the tags «fixed», «adapt-static», and «adapt-dyn» as attributes of generalization relationships.
The «adapt-static» tag is applied to a generalization to express the idea that adding application-specific classes during design time is allowed. This tag allows the addition of new subclasses with application-specific features. In particular, a generalization relationship should be marked by «adapt-static» if the corresponding superclass should be adapted in subclasses. Figure 3.18 illustrates the use of this UML-F tag in the context of a generalization. As there is no detailed information about how and why to extend the generalization relationship, the information conveyed with the «adapt-static» tag is somewhat weak. Therefore, for example, the tags defined for the Composite pattern are based on the «adapt-static» tag, but include additional information on how to add classes to adapt the inheritance structure, as shown in Chapter 4.
Sometimes a framework is designed in such a way that no direct subclass is permitted to be added to a given class. Figure 3.19 illustrates the constraint imposed by the «fixed» tag.
As a second example, in the Visitor pattern (Figure 3.20, adapted from Gamma et al. 1995), an instance of class Visitor navigates through an object structure. The classes representing the objects that form the object structure should
As demonstrated above, the UML-F tag «fixed» expresses a restriction on the generalization relationship. The application developer cannot add direct subclasses, but can still add application-specific subclasses to already existing framework subclasses. The class diagram in Figure 3.21 exemplifies the abstract syntax of a programming language. Though the upper level generalization relationship is «fixed», the generalization relations between the subclasses of Nonterminal and their subclasses have the attribute «adapt-static», which allows language-specific extensions in the predefined Nonterminal categories Expression and Statement.
If the tag «adapt-dyn» is attached to a generalization, subclasses may be added during runtime. If a language and its runtime system support such extensions, an adaptation during runtime carries a risk of behavioral errors and exceptions that needs to be dealt with. Usually, the «adapt-dyn» tag is attached to a method, its defining class, and the generalization relationship altogether (see Figure 3.22). Omissions are possible because an attachment to the method is sufficient to
If no tag is explicitly specified for a generalization, then the tag that implicitly applies depends on the tag of the superclass in that generalization. If the superclass is marked as «adapt-dyn» then the generalization is marked as «adapt-dyn». If the superclass is «fixed» or «adapt-static» then the generalization is «adapt-static». An explicit tag for the generalization, such as «adapt-static», always overrides the (invisible) promoted default.
As a convention, every class has a subclass tree and is therefore the root of a generalization relationship. If the subclass tree is empty, the generalization is empty too, but it still exists. Figure 3.23(a) shows how to mark a generalization relationship so that no subclasses can be defined. However, for this special case, UML-F provides a more intuitive notation, as shown in Figure 3.23(b). This is in accordance with the Java language which provides the modifier 'final'
the class is «fixed»
the attached generalization relationship is «fixed»
the generalization is empty - that is, no subclasses exist.
Standard UML already provides some constraints on generalizations.
[15]
The
complete
constraint
[15] It was previously explained that the distinction between constraints, stereotypes, and tagged values is not sufficiently clear in the standard UML. UML-F therefore only uses tags - even for concepts similar to UML constraints.