Flylib.com

Books Software

 
 
 

3.5 UML-F framework tags

3.5 UML-F framework tags

The UML-F tags in the above sections are useful for any object-oriented model including frameworks. This section presents basic UML-F tags that are only useful in the context of frameworks. They are used by the framework developer to document the intended use of certain framework concepts for the application developer. These tags therefore give guidance to the application developer through the framework adaptation process. Furthermore, they give framework developers guidance for the enhancement of a framework, as they also allow one to document intentions to codevelopers. It is important to bear in mind the fact that framework developers are the masters of the framework and can therefore change the framework and its decorating tags, whereas application developers may not.

3.5.1 Framework and application classes

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 themselves are usually not subject to change.

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 generally adapted through the definition of new subclasses and the implementation of interfaces. A third category of classes belongs to the utility level, as these classes are provided as basic classes by utility libraries or the runtime system. These may be tagged «utility». Figure 3.11 exemplifies their usage. If a package is marked with one of these tags, then all of its classes and interfaces are implicitly marked as such. Table 3.8 summarizes the meaning of these UML-F tags.

Figure 3.11. «framework», «application», and «utility» tags
graphics/03fig11.gif
Table 3.8. UML-F class tags for discerning between framework and application components
Tag name 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.

3.5.2 Overview of adaptation tags

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, methods , and interfaces are adapted through the definition of new (sub)classes and the implementation of interfaces. The tags in Table 3.9 express whether an element is fixed (tag «fixed») or whether it can be adapted in subclasses. Two kinds of adaptations are distinguished: those made during runtime of a system (tag «adapt-dyn»); and those made during the design or evolution of a system (tag «adapt-static»). The following subsections discuss these tags in more detail.

Table 3.9. UML-F tags for basic framework adaptations
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 meta-information system and the dynamic loading of classes.

3.5.3 Method adaptation tags

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.

The «fixed» tag

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, neither during runtime nor during design time.

[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 implementations of that method are not allowed. Here, the «fixed» tag differs from the Java modifier final, which does not even allow framework subclasses to override a method. UML-F allows overriding of a «fixed» method within the framework, but the «fixed» tag also holds for the overridden subclass methods so that the application components cannot override it. Figures 3.12 and 3.13 illustrate the semantics of the UML-F tag «fixed».

Figure 3.12. Constraint on overriding a «fixed» method
graphics/03fig12.gif
Figure 3.13. Propagation of «fixed» tag to subclasses
graphics/03fig13.gif
The «adapt-static» tag

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 dummy implementation. It is also possible that the default implementation of the method is rather elaborate and should be used through a super()-call if overridden. [13]

[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».

Figure 3.14. Overriding «adapt-static» methods
graphics/03fig14.gif
The «adapt-dyn» tag

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 overrides an «adapt-dyn» method can be loaded while the system is running. A command interpreter is a workaround for dynamic adaptation in languages without an appropriate meta-information system (such as C++) and with runtime systems which lack dynamic loading capabilities. The dynamic adaptation is achieved by changing the command string.

Dynamic loading of methods introduces some overhead. So one should check carefully if the application of this technique is necessary. Furthermore, if the framework tags a method with «adapt-dyn», the framework itself should provide either a mechanism to load respective subclasses dynamically or an adaptable command interpreter as part of the framework.

Defaults

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 meant to be «fixed». For methods, the «fixed» tag does carry over to subclasses, whereas the other two tags do not. Therefore an «adapt-dyn» method may be overridden by a «fixed» method in a subclass, but not vice versa.

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 methodological guidelines that accompany a good framework - for example, in the form of a cookbook - should provide hints about adaptations for application-specific requirements.

3.5.4 Tags in the context of classes and interfaces

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.

The «fixed» tag

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.

Figure 3.15. Marking a class as «fixed»
graphics/03fig15.gif

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 core classes in the inheritance hierarchy, whose methods are not intended for adaptations, and to highly specialized classes that have no subclasses at all (e.g. the String class in Java).

Figure 3.16. Tag «fixed» is not inherited directly between classes
graphics/03fig16.gif
The «adapt-static» tag

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 restrictive than the class tag (see Figure 3.17). This means that if a class has the tag «adapt-static», then all its methods must be «adapt-static» or «fixed».

[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.

Figure 3.17. Applying «fixed» and «adapt-static» tags to classes
graphics/03fig17.gif
The «adapt-dyn» tag

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.

3.5.5 Tags in the context of generalization

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

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.

Figure 3.18. Explicit marking of a generalization
graphics/03fig18.gif
The «fixed» tag

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.

Figure 3.19. Semantics of a «fixed» generalization
graphics/03fig19.gif

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 incorporate a fixed generalization if no additional subclasses of class Element are expected to be added. Another reason to prevent a generalization to be extended by the application developer is the existence of appropriate mechanisms to adapt its classes by composition or parameterization.

Figure 3.20. A sample «fixed» generalization in the Visitor pattern
graphics/03fig20.gif

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.

Figure 3.21. Combination of fixed and adaptable generalizations
graphics/03fig21.gif
The «adapt-dyn» tag

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 convey the desired information.

Figure 3.22. An «adapt -dyn» constellation
graphics/03fig22.gif
Promoting a tag from the superclass

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' applicable to classes, indicating three constraints:

Figure 3.23. Equivalent representations ruling out the definition of subclasses
graphics/03fig23.gif
  • 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 roughly corresponds to UML-F's «fixed» tag, but with one important difference - the complete constraint applied to a class carries over to all subclasses and, therefore, does not allow the framework developer to decide which subclasses are adaptable. Thus, the inheritance structure of Figure 3.21 cannot be expressed within standard UML. The effect of the complete constraint, on the other hand, can be recreated by applying the «fixed» tag to all subclasses as well.

[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.