Chapter 5: Extension of Industry Standards

Home - Sitemap - About » Computer Science - Research - Dissertation
Computer Science
Research, Industry Work,
Programming
Community Service
Hillside Group, CHOOSE,
Stanford GSA
The Serious Side
Business School,
Learning Chinese
Humorous Takes
Switzerland, United States,
Software, Fun Photos
Travel Stories
Europe, United States, Asia
  
Living Places
Berlin (+ Gallery), Zürich
Boston, S.F. + Bay Area

Role modeling for framework design is a new technique, for which no standard exists. Current industry design notations and programming languages provide no direct support. While standard design notations and programming languages provide classes and objects, they do not support role types, role constraints, and role models. However, standards are important, because they provide developers with a shared vocabulary and tool support. This chapter discusses how to extend industry-standard design notations and programming languages with role modeling concepts. Extensions are provided for UML, Java, C++, and Smalltalk.

5.1 Chapter overview and motivation

Role modeling for framework design introduces new concepts and extends established ones. Effectively, it forms a new design method. However, many of the concepts cannot be expressed directly using industry-standard design notations and programming languages.

Some of the new or revised concepts map well on concepts of current design notations and programming language standards. For example, the concept of class maps well on the UML concepts of interface or class. Other concepts, for example, the role model concept, are more difficult to map, because no equivalent concept exists.

Role modeling for framework design is only a part of what could be a full-blown design method. It ignores many issues that are needed for an industrial-strength design method. One possible conclusion might therefore be to develop a new full-blown design notation and programming language that directly supports role modeling.

This approach has the following advantages:

  • Developers can directly express framework designs. No mental gap between the concepts behind a design and its expression using a specific notation must be maintained.
  • Given adequate tools, framework design and implementation can be checked for conformance based on the new concepts. Such checks may significantly reduce the error rate.

However, this approach also has the following disadvantages:

  • Role modeling for framework design is an evolutionary addition to existing approaches. It does not try to replace existing standards and should therefore extend rather than replace them.
  • The development of a full-blown industrial-strength modeling approach requires a substantial amount of work. A new approach would distinguish itself only through its new framework design concepts.
  • Developers would have to learn yet another new method. This increases the time until they productively join a project and makes it more difficult to find people that are willing to maintain a system.
  • No tool support or no tool support comparable to the support for mainstream notations is available.

For practical purposes, it is preferable to extend existing industry standards with the new role modeling concepts rather than to introduce yet another approach that is incompatible with the existing ones. An extended industry standard provides the new concepts and still lets developers get to work quickly. This approach faces less developer resistance, with tool support being available right from the beginning.

A dedicated design notation and programming language for role modeling can be introduced later, should role modeling for framework design reach widespread acceptance. In fact, role programming techniques like [Van97, VN96] and general purpose programming language extensions like aspect-oriented programming [KLM+97] already point into that direction.

This chapter discusses how to extend industrial-strength design notations and programming languages with the new role modeling concepts. First, it discusses requirements and properties common to all these extensions. Then, the chapter introduces an extension of one design notation, UML. Finally, it provides extensions of the programming languages Java, C++, and Smalltalk.

5.2 Common properties

This section discusses how industry standards can be extended with role modeling concepts, and how mappings between standards ease the introduction of a new extension. The section also lists properties that should be common to all role modeling extensions of existing industry standards. It reviews the concepts that must be provided by each such extension and introduces a simplified version of the Figure framework example.

5.2.1 Extending an industry standard

A successful extension of an industry standard is only possible if the extension blends in well with the existing concepts and their idiomatic use. Otherwise, the extension would feel alien to the developers and be rejected. Fortunately, role modeling for framework design was intended to be an extension of known concepts right from the beginning.

As a consequence, the industry standard will take on the dominant role, and the role modeling concepts that extend it must come natural to users of the standard. They must be expressed in terms of the standard's concepts or extension mechanisms. Chapters 3 and 4 define and discuss the role modeling concepts in standard-neutral terms to ease the extension of different standards and put them onto a common basis.

Figure 5-1 illustrates how the role modeling concepts extend the four standards discussed in this chapter (UML, Java, Smalltalk, and C++).

Figure 5-1: Extension of industry standards with role modeling concepts.

Figure 5.1 illustrates two types of relationships: extensions and mappings.

  • Extensions are role modeling extensions of the respective industry standard. For example, UML Role Modeling is an extension of UML within the confines of UML. An extension is the definition of role modeling concepts in terms of the existing standard's concepts and extension mechanisms.
  • Mappings are mappings between the general role modeling concepts and a role modeling extension of a specific industry standard. A mapping is a function that explains how a role modeling concept is represented in a specific standard.

Being explicitly aware of the mappings is important, because it helps introduce new industry standard extensions quickly and without much overhead. For example, if a new programming language is defined, and UML tool support for that language exists, the extension of the new programming language with role modeling concepts can be described as the composition of three functions MRM/UML, EUML/RM, and MUML/PL:

  • MRM/UML is the mapping from the general role modeling concepts to the UML Role Modeling concepts.
  • EUML/RM is the extension of the UML concepts with the UML Role Modeling concepts.
  • MUML/PL is the mapping between UML and the new programming language.

A more detailed and direct extension of the programming language can be introduced later.

The mappings are typically one-way mappings. Depending on the target of the mapping, it cannot be guaranteed that an inverse mapping exists. In any such mapping, information from the domain is lost. This is one reason, why it is better to provide direct extensions of the programming languages rather than using a mapping detour through UML as just illustrated.

The other reason is that it is more convenient to use a direct extension rather than carrying out the composition of three mappings. Still, if a new programming language is introduced, for which no direct extension exists yet, developers can use this mechanism.

5.2.2 General requirements

The extension of an industry standard with role modeling concepts must not only be as precise as possible, but it must also match the way developers work with the extended standard and the base standard.

Ideally, the following requirements are fulfilled when defining an extension:

  • Robustness. A design or program based on the extended standard must be robust with respect to interpretation and handling by developers who do not understand the extension. Developers must still be able to work on the design.
  • Inconsistency. Also, a design that is inconsistent from the extension's point of view must not block the system in any respect (for example, browsing, type checking, or code generation). As a corollary to the robustness requirement, developers must still be allowed to work productively with an inconsistent design.
  • Incompleteness. The design must allow evolutionary adding of elements based on the extended standard rather than requiring that the design be fully compliant with the extension right from the start. Thus, the extension must allow for its partial application.
  • Tool support. Non-trivial designs require tool support, such as Rational Rose, Paradigm Plus, Visio, etc. The extended standard must take into account how users handle designs visually. (This is one reason why, for example, the UML notation guide describes a concrete visual syntax rather than an abstract syntax only.)
  • Bridge between design and implementation. An extended standard should not complicate bridging between design and implementation. A design usually keeps evolving close to its implementation. Developers avoid making the gap between design and implementation too large to reduce intellectual mismatch.

Providing robustness, dealing with inconsistency, and dealing with incompleteness ensures a low entry hurdle to using role modeling. Because designs may be incomplete, the initial investment may scale from zero to the full application of the method. Because a design may be inconsistent from a role modeling perspective, the whole system (tool + design) stays operational at any point in time.

Providing robustness is the hardest requirement. It prevents that the mapping introduces additional complexity and uncommon semantics into a design that is otherwise easily interpretable using general knowledge of the underlying design notation.

5.2.3 Handling role types and role models

UML, Java, C++, and Smalltalk provide support for classes and their packaging, next to a wealth of other concepts. Role modeling for framework design requires support not only for classes, but also for role types, role constraints, role models, class models, and frameworks. None of the industry standards discussed in this chapter provides native support for these concepts.

However, developers always have modeled and implemented class models and frameworks. They typically use the standard's packaging mechanism to define what goes into a class model or framework, and they use the naming mechanism and conventions of the standard to separate different models. We can therefore use the packaging mechanism to define role models, class models, and frameworks.

Each section of this chapter on extending a specific standard shows how the role modeling concepts of class, role model, class model, and framework are represented using native concepts. The representation is typically based on the standard's concept of type, class, and packaging.

The primary challenge is to adequately extend a standard with the role type, role constraint, and role model concept, and to define how they relate to classes and class models. For each standard, this is done differently. Role constraints do not require much discussion, but role types do, due to the many different ways of using them.

Let us assume that a standard provides a simple means to represent a type, for example a UML or Java interface. A role type could then be expressed using an interface. However, different kinds of role types have different pragmatics, and it is questionable whether one concept of interface fits all uses of the role type concept. Moreover, given the high number of role types in a design, the naive approach faces an explosion in the number of interfaces.

For expressing role types, we face the following options:

  • Leave role types implicit in a class interface.
  • Make role types explicit in a class interface by annotating them (for example, through method categories).
  • Make role types explicit as interfaces of their own.

For expressing role models, we face the following options:

  • Leave role models implicit by leaving all of their role types implicit.
  • Make a role model explicit as part of a class model by packaging role models with their class model.
  • Make a role model explicit and reusable for different class models by packaging it on its own.

Design and implementation use these options differently. In design, typically every important design decision should be recorded and expressed. In implementation, only all implementation decisions should be explicit, but there is no need to directly reflect the full design.

The design notation and the programming language sections introduce their respective rules below.

5.2.4 Figure framework (example)

This chapter uses a simplified version of the Figure framework as its example. Figure 5-2 shows its design.

Figure 5-2: The (simplified) Figure framework.

The Figure framework comprises two classes (Figure and CompositeFigure), builds on the Graphics class, and extends the Object framework. The dynamics of the framework are defined by the ObjectProperty, Figure, FigureChain, FigureObserver, FigureHierarchy, and Graphics role models.

The framework is discussed in depth in Chapters 3 and 4. It offers all constellations that we need to discuss.

  • ObjectProperty is a reusable role model. All other role models are non-reusable.
  • Figure.Figure, FigureObserver.Subject, etc. are regular role types without further qualification.
  • FigureObserver.Observer is a free role type that has operations.
  • Figure.Client, FigureHierarchy.Client, and Graphics.Client are free no-op role types.

Sections 5.3 and 5.4 show how the framework is expressed in UML and implemented in Java, C++, and Smalltalk.

5.3 Design notations

This section discusses how to extend a design notation. It provides one example: the extension of UML with role modeling concepts for framework design. The discussion omits the trivial parts and focuses on the higher-level concepts of role type, role constraint, class, role model, class model, and framework.

5.3.1 Extending design notations

A design notation must provide means to record and document all relevant design decisions. In contrast to programming languages, which usually let developers make only parts of a design explicit in the implementation, should a design notation let developers record all relevant information (whether they do so is another question).

Therefore, the extension of a design notation must cater for all new or revised modeling concepts, including role type, class, role constraint, role model, class model, and framework. Also, it should be possible to express variations of specific design concepts, like free role types and no-operation role types.

This section does not discuss how to express the dynamic behavior of objects acting according to a class type or role type. The definition of such a notation is left to the available type specification mechanisms. UML, for example, offers object and sequence diagrams to illustrate runtime object behavior. Also, more elaborate approaches like Extended-Event-Traces [BHKS97] or others can be used.

The rules for expressing role models, class models, and frameworks on a design level are simple:

  • Class model. A class model should be represented explicitly. It gets at least one package. For structuring complex models, more packages can be used.
  • Reusable role model. A reusable role model should be represented explicitly. It should be packaged independently of a specific class model in which it is used, for example, in a package of reusable role models.
  • Non-reusable role model. A role model that is used once in a class model need not be packaged on its own. It should go into the class model's package. Within that package, it might get its own diagram or not, depending on the significance of the role model.

Role types of a reusable role model should always be made explicit, both in design and implementation.

For non-reusable role models, the situation is more complicated. As discussed, we can qualify role types as being free and/or no-op. These properties lead to the following kinds of role types and their rules for being left implicit or made explicit:

  • Role types. Regular non-free role types with operations should be made explicit, because they represent an important design aspect that needs to documented explicitly.
  • Free role types (both no-op and with operations). Free role types should be made explicit, because they may be picked up by more than one client class, and because they represent an important design aspect.
  • Non-free no-op role type. A non-free no-op role type may or may not be made explicit. It is only used within the context of the current class model; hence the number of uses is fixed.

Thus, role types of a non-reusable role model should be made explicit if they are regular or free role types. If they are non-free no-op role types, a developer may decide to not explicitly represent a role type but to reduce it to an annotation of the class providing the role type.

5.3.2 Extending UML with role modeling

UML offers a rich metamodel for modeling object systems, which makes it easy to extend it with role modeling concepts. The extension of UML with role modeling concepts relies only on three basic UML concepts: Class, Interface, and Stereotype.

  • A class maps either on a UML class or a UML interface. Whether a class or an interface is chosen depends on the complexity of implementing the class. A lightweight class is typically represented as a UML class, while a heavyweight class, for which several implementations exist, or which has many role types, is represented as a UML interface.
  • A role type maps on a UML interface tagged with the "RoleType" stereotype. Such an interface is called a role-type interface. A free role type is represented as a "FreeRoleType" interface, a no-op role type as a "NoopRoleType" interface, and a free no-op role type as a "FreeNoopRoleType" interface.
  • A role constraint between two role types is mapped on a textual annotation of the relationship between the two role types. If no annotation is provided, the default case (role-dontcare) is assumed.
  • A role model or a class model maps on a class diagram. There is no need to make a class diagram for every role model, but if a role model is reusable, it should get its own class diagram.
  • A framework also maps on a class diagram. In addition, it might be packaged as its own component, drawing visible boundaries to the outside.
Figure 5-3: The ObjectProperty role model as a UML class diagram.

When working with UML Role Modeling, developers use UML classes and interfaces as they are used to. If needed, they add additional role type and role model information. They tag interfaces and classes as specific kinds of role types, and make the role models explicit by connecting role-type interfaces and classes.

Figure 5-3 shows the ObjectProperty role model using a UML class diagram. It uses the color coding introduced earlier: free role types have a light-gray background.

The ObjectProperty role model is a reusable role model that should be packaged on its own, or, at least, should get its own diagram. Property.Client, Property.Provider, and Property.Property are role-type interfaces. Because the ObjectProperty role model is a reusable role model, all of the role types are free. In addition, the Property.Client and the Property.Property role types are no-op role types.

Figure 5-4: The Figure framework as a UML class diagram.

Please also observe the use of the class InvalidPropertyException without any further qualification. This reflects the pragmatic approach of robustly embedding the role modeling concepts into an existing standard. From a UML perspective, the role-type interfaces are just interfaces that relate to other interfaces and classes without further qualification.

Figure 5-4 shows the Figure framework using a UML class diagram. In addition to coloring free role types in light-gray, it colors role modeling classes in dark-gray. Please note that Figure and CompositeFigure are conceptually classes from a role modeling perspective, but are represented as UML interfaces. As explained, this decision is a matter of expected convenience of implementation.

Figure 5-4 shows how different kinds of role types are represented:

  • Examples of role-type interfaces are FigureHierarchy.Child and FigureChain.Predecessor.
  • Examples of free role-type interfaces are FigureObserver.Observer and ObjectProperty.Provider.
  • There are no examples of non-free no-op role type interfaces.
  • Examples of free no-op role type interfaces Figure.Client and FigureHierarchy.Client.

Most of the role models bound to this class model are not made explicit. They exist only in this diagram, and are implicitly represented by the naming convention of their role types.

The role modeling classes Figure and CompositeFigure are represented as UML interfaces, while the (simplified) helper class Request is represented as a UML class.

5.3.3 Extension properties

The following discussion shows how the extension properties asked for in Section 5.2 are realized by the UML role modeling extension. It thereby describes the rationale behind the extension.

  • Robustness. The interpretation of a UML Role Modeling class diagram can be done by any developer who knows interfaces and classes, and who is told that a roletype interface represents a type that is part of the class type it is extended by. This is the minimal effort required.
  • Inconsistency. Standard UML allows classes and interfaces to directly refer to each other. Thus, role-type interfaces may refer to other UML classes and interfaces, without an intermediate role-type interface. From a role modeling perspective, such direct reference indicates that the design may not yet be done and that further elaboration will provide the missing role types.
  • Incompleteness. Designs may be inconsistent, and therefore incomplete.
  • Tool support. The mapping uses only basic UML concepts. These concepts are the first to be provided by a UML based modeling tool, so that tool support for UML Role Modeling is readily available, even if the tool supports only a subset of standard UML. If code generation needs to be supported in more detail, additional stereotyping of interfaces can provide missing information and control back-end code generation.
  • Bridge to implementation. The mapping supports modeling close to the implementation level. In an implementation, a UML interface typically maps onto the interface concept of a programming language, while a UML class directly maps on an implementation class. Mapping a role modeling class on a UML interface or class lets developers make this distinction on the design level and supports code generation.

The definition of the mapping uses the most common abstractions only, because developers best know them, and because the UML definition still evolves, as it is imprecise and unclear in many instances [BHH+97].

5.4 Programming languages

Every framework is eventually implemented using a programming language, independently of whether it was designed using role modeling or not. Typically, programming languages provide only meager support for representing design-level concepts that go beyond individual classes. This section analyses which of the role modeling concepts to map to a programming language and how to do so. The section covers Java, C++, and Smalltalk.

5.4.1 Extending programming languages

Programming languages provide mechanisms for source code definition and structuring. Designers of programming languages typically do not include higher-level design concepts like role model, class model, or framework in the language definition. These concepts are left to the design level of software system development, as opposed to the implementation level, the support of which is the primary concern of programming language designers. All programming languages covered in this section exhibit this property.

The distinction between design and implementation relaxes the situation. While a design must capture all relevant design details, an implementation must only provide a proper implementation of the design, but not the design itself. Hence, there is no need to extend a programming language with all role modeling concepts. Rather, it is sufficient to provide those concepts that let us implement a design in such a way that we find our way back to the design documentation and do not have to bridge too far a mental gap when doing so.

For identifying role models, class models, and frameworks, the packaging mechanism of a given programming language can be used. Implementation-level packaging follows the same rules as design-level packaging:

  • A class model gets its own package (at least one).
  • A reusable role model gets its own package.
  • A non-reusable role model becomes part of the package of a class model.

Implementation-level role types can be expressed using interfaces or equivalent concepts. All role types of reusable role models should be explicit. For role types from non-reusable role models, the following rules apply:

  • Role type. A regular role type need not be made explicit as an interface. It can be merged with the class interface. It is helpful, though, to annotate the operations of a class interface with the role type names.
  • Free role type. A free role type with operations must be represented explicitly, so that clients can pick up the role type by inheriting from it or copying it.
  • No-op role type. A no-op role type that is not free need not be made explicit. However, it is helpful to annotate a class as providing a particular no-op role type.
  • Free no-op role type. A free no-op role type may or may not be made explicit. Because the role type is free, it is important to see it explicitly. Because it is no-op, simply tagging a client class might be sufficient.

A class should list all of its role types, including no-op role types, and it should annotate an operation with the role type name from which it is derived. If a role type is explicit, this happens automatically. If a role type is not made explicit, developers need to do this by hand.

Role constraints are not made explicit. This feature of the role modeling technique is most useful on the design level to communicate the idea behind a role model. It is left to the implementation of a class to ensure that a role constraint is maintained. The role types or class interfaces involved may document the constraint, though.

To support the annotation of class interface with role type and role model information, specialized documentation tools can be used. For example, Java's javadoc and its variants in other programming languages provide lightweight annotation features that support documentation, code generation, and round-trip engineering between design and implementation.

5.4.2 Problems of programming language extension

The pragmatic extension of programming languages described above is too weak for properly checking the conformance of a design with its implementation. Such conformance checking is desirable, because it lets us catch implementation errors as early as at compile-time. However, conformance checking is typically done only for very specialized areas of software system design. The critique therefore applies to the whole of object-oriented modeling.

More immediate problems occur due to the composition of role types to form a class type. Most programming languages do not support type specification and composition. Eiffel is an exception of a close-to-mainstream programming language that provides several useful features of this kind, in particular a lightweight type specification mechanism (design by contract [Mey91, Mey92b]), and a renaming feature [Mey92].

The following problems arise from the need to compose role types:

  • Name clashes. Different role types might define two identical operations. However, their semantics is different, and they are used for different purposes. The operations must be distinguished, and the naming conflict must be resolved.
  • Redundant operations. Different role types might define operations that have different signatures, but the same implementation. In the Figure framework example, operations getParent and getSuccessor always return the same object reference, making one implementation redundant.
  • Mapping of abstract state on implementation state. Each role type comes with its own state space definition. When composing role types, the state spaces of the role types must be mapped on the single state space of the class type, for which a proper implementation state space must be defined.

The first problem, name clashes, is a common problem that is independent of the approach presented here. Some programming languages offer explicit renaming features. If no such feature is available, the names must be changed to avoid the conflict. Usually, the operation names are pre- or postfixed to indicate their origin.

The second problem, redundant operations, is not a real problem, but rather an issue of style. Should a redundant operation be removed? Names are important, even if two operations do the same. Each client expects operation names that are best suited for the task at hand. Therefore, redundant operations should not be removed. Rather, they should be named well and implemented using common primitive operations.

The last problem, the definition of the implementation state space of a class, results from the larger problem of type composition. The programming languages considered here describe the semantics of some operation using prose. The only aspects described formally are the parameters and return values of the operations of a role type. They define the abstract state space that clients see of an object playing a role defined by the role type.

A developer of a class must define how the abstract state spaces of the role types are composed to form the abstract state space of the class. For the abstract state space of a class, the developer must then find a suitable and efficient implementation. In practice, the intermediate step of defining the abstract state space of a class is not explicit, and a class is implemented by deriving its implementation state space directly from the role types.

In the end, this is not a problem that can be treated on a general level, and it is up to every developer to find a suitable implementation state that lets him or her efficiently implement the different role types. Frequently, a class internal set of primitive operations is used by the role type implementations to manipulate the common implementation state. Also, the implementations of role type operations call each other to communicate a state change. Implementing this is an issue of proper programming practice, and not considered further here.

5.4.3 Extending Java with role modeling

Java provides interfaces, classes, and packages as concepts that can be used to implement role-model-based designs. Java interfaces and classes can be used to represent role types and classes, and Java packages can be used to provide a namespace for role models, class models, and frameworks.

A role type is represented as a Java interface. A class is represented as a Java interface or class, depending on its expected implementation complexity. Reusable role models and class models are packaged as individual units.

Specification 5-1 shows the source code of the Java Figure interface.

package org.riehle.diss.Figure;

import java.util.Enumeration;
import java.awt.Graphics;
import java.awt.Point;
import org.riehle.diss.ObjectProperty.*;

/**
A Figure object is a graphical figure object that
can be used in drawing editors. It provides the
following role types:

- Figure.Figure
- FigureHierarchy.Child
- FigureObserver.Subject
- FigureChain.Predecessor
- Graphics.Client
- ObjectProperty.Provider
**/

public interface Figure extends PropertyProvider {
    /**
    @roletype Figure.Figure
    **/
    public void draw(Graphics gc);
    public void drawOutline(Graphics gc);
    public Point getOrigin();
    public void setOrigin(Point origin);
    public Point getExtent();
    public void setExtent(Point extent);
    public void place(Point location);
    public void move(int dx, int dy);
    public void resize(int handle, int dx, int dy);
    
    /**
    @roletype FigureHierarchy.Child
    **/
    public boolean hasParent();
    public CompositeFigure getParent();
    public boolean maySetParent();
    public void setParent(CompositeFigure parent);
    
    /**
    @roletype FigureObserver.Subject
    **/
    public boolean hasFigureListener(FigureListener listener);
    public void addFigureListener(FigureListener listener);
    public void removeFigureListener(FigureListener listener);

    /**
    @roletype FigureChain.Predecessor
    **/
    public CompositeFigure getSuccessor();
    public boolean maySetSuccessor();
    public void setSuccessor(CompositeFigure successor);

    /**
    @roletype Graphics.Client
    @properties free noop
    **/
}
Specification 5-1: Definition of the Java Figure interface to represent the Figure class.

A role type that is to be explicit is represented as a Java interface. The use of abstract classes as a representation mechanism of a free role type does not make sense, because Java classes are restricted to single inheritance. The interface should be named after the role type.

Using customary Java naming, Specification 5-2 shows the free role type FigureObserver.Observer.

package org.riehle.diss.Figure;

import java.util.EventListener;

/**
This interface represents the FigureObserver.Observer role type.

A FigureListener is an object that is notified by a Figure object
about state changes of that Figure object.

@roletype FigureObserver.Observer
@properties free
**/

public interface FigureListener extends EventListener {
    public void figureChanged(FigureEvent e);
    public void figureRemoved(FigureEvent e);
    public void figureInvalidated(FigureEvent e);
}
Specification 5-2: Definition of the Java FigureListener interface to represent the FigureObserver.Observer role type.

A free no-op role type of a class model may be represented as an empty Java interface (or not at all). For example, the Figure class provides the Graphics.Client role type. This is a free no-op role type of the Graphics framework. The provision of the role type can be annotated in the class interface as illustrated in Specification 5-3:

public interface Figure extends PropertyProvider {
    ...

    /**
    @roletype Graphics.Client
    @properties free noop
    **/
}
Specification 5-3: Role type Graphics.Client in the definition of the Java Figure interface.

Examples of where it makes sense to explicitly represent a free role type without operations are client role types for object creation as presented in Specification 5-4. The comments in the client interface describe the ordering criteria imposed on the client of operation invocations on the newly created object.

package org.riehle.diss.Figure;

/**
This interface represents the GroupFigureCreation.Client role type.

@roletype GroupFigureCreation.Client
@properties noop
**/

public interface GroupFigureCreationClient {
    // After calling a GroupFigure constructor, you may
    // add and remove as many Child objects as you wish.
    // You must finalize the initialization of a GroupFigure
    // with a call to initDone(). After this, you cannot
    // add or remove any Child object, unless you call
    // reinitialize() to reopen the GroupFigure object.
    // Calling reinitialize() will cause the GroupFigure
    // to drop all contained Figure objects.
}
Specification 5-4: Definition of the Java GroupFigureCreationClient interface to represent the GroupFigureCreation.Client role type.

A role type of a reusable role model is represented as a Java interface. The same argument that applies to a free role type of a class model applies here as well. Specification 5-5 shows the ObjectProperty.Provider role type.

package org.riehle.diss.ObjectProperty;

import java.util.Enumeration;

/**
This interface represents the ObjectProperty.Provider role type.

A Provider provides Object instances as Properties. A Property
has a name. A Client may ask for a property by name. It may set 
a Property to the Provider using the Property's name. It may
request all names of accepted Property types and may test whether
a certain Property type is known and acceptable to the Provider.

@roletype ObjectProperty.Provider
@properties free
**/

public interface PropertyProvider {
    public boolean hasProperty(String name);
    public boolean acceptsProperty(String name, Object prop);
    public Object getProperty(String name);
    public Object getDefaultProperty(String name);
    public Enumeration getProperties(); // collection of Object

    public boolean acceptsPropertyType(String typeName);
    public Enumeration getPropertyTypeNames(); // collection of String
 
    public void setProperty(String name, Object prop)
        throws InvalidPropertyException;
    public void unsetProperty(String name);
}
Specification 5-5: Definition of the Java PropertyProvider interface to represent the ObjectProperty.Provider role type.

Classes are simpler to represent. They are expressed either as a Java interface or a class. Which variant to choose depends on the number of implementations of the design-level class [Rie97d, Rie97e, RD99a, RD99b]. In the example, the class Figure is represented as a Java interface, for which different Java classes exist as different implementations.

In both cases, it is possible to tie in the different role types that a class must provide. If the class is represented as a Java interface, it inherits from the interfaces that represent (some of) the role types it is to provide. If the class is represented as a Java class, it simply implements them. In addition, non-reusable role types are textually directly embedded into the Java interface or the Java class.

Role models that are considered reusable should get their own Java package. The package introduces a convenient namespace for the role types of the role model. A framework should also get its own package. It ties in reusable role models by means of import statements. In the example, ObjectProperty is considered a reusable role model and thus gets a package of its own. The Figure framework also gets its own package, from which it imports these two other packages.

5.4.4 Extending C++ with role modeling

In contrast to Java, C++ does not offer an explicit interface concept. However, it provides the concept of pure virtual functions (polymorphic operations without implementation) and multiple inheritance. Taken together, these concepts can be used to emulate the Java interface concept. This also meets the intent of the designers of the C++ programming language [Str94].

The Java ObjectProperty interface from above is expressed in C++ as shown in Specification 5-6.

#include "Object.h"
#include "String.h"
#include 

/**
This interface represents the ObjectProperty.Provider role type.

A Provider provides Object instances as Properties. A Property
has a name. A Client may ask for a property by name. It may set 
a Property to the Provider using the Property's name. It may
request all names of accepted Property types and may test whether
a certain Property type is known and acceptable to the Provider.

@roletype ObjectProperty.Provider
@properties free
**/

class PropertyProvider : public Object {
    public bool hasProperty(string name) =0;
    public bool acceptsProperty(string name, Object* prop) =0;
    public Object* getProperty(string name) =0;
    public Object* getDefaultProperty(string name) =0;
    public vector<Object*>::iterator getProperties() =0;

    public bool acceptsPropertyType(string typeName) =0;
    public vector<string>::iterator getPropertyTypeNames() =0;

    public void setProperty(string name, Object* prop)
        throw InvalidPropertyException =0;
    public void unsetProperty(string name) =0;
};
Specification 5-6: Definition of the C++ PropertyProvider interface to represent the ObjectProperty.Provider role type.

Using this substitute for Java interfaces, the whole Java discussion also applies to C++. The Java implements relationship becomes a regular inheritance relationship. C++'s inheritance mechanisms allows multiple inheritance, so that there are no constraints on the number of interfaces that might represent a role type and that might be provided by a class.

Packaging in C++ is done using files and name spaces. The namespace keyword lets developers scope classes with a name space so that references to these classes must be qualified using the name of the name space.

5.4.5 Extending Smalltalk with role modeling

The representation of the role modeling concepts in Smalltalk differs from the one used for Java and C++. Smalltalk does not provide interfaces, multiple inheritance, or name spaces. The following discussion uses a generic Smalltalk dialect and ignores vendor-specific extensions.

In Smalltalk, it is a convention to express abstract methods by implementing them with the statement self subclassResponsibility. When executed, this statement invokes the subclassResponsibility method of the object, which will usually bring up the debugger to indicate that a method was executed for which a subclass failed to provide an implementation.

A class with only abstract methods is an abstract class. It can be used to represent a role type. Specification 5-7 describes the role type FigureObserver.Observer in Smalltalk.

"A FigureObserver is an object that is notified by a
Figure object about state changes of that Figure object.

@roletype FigureObserver.Observer
@properties free"

Object subclass: # FigureObserver
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''

"FigureObserver publicMethods"

figureChanged: figureEvent
    self subclassResponsibility

figureRemoved: figureEvent
    self subclassResponsibility

figureInvalidated: figureEvent
    self subclassResponsibility
Specification 5-7: Definition of the abstract FigureObserver class in Smalltalk to represent the FigureObserver.Observer role type.

A class composes role types. Java and C++ do this using multiple inheritance. Smalltalk does not provide multiple inheritance, at least not the mainstream Smalltalk implementations VisualWorks and VisualAge. Smalltalk's elaborate metalevel architecture makes it possible to introduce multiple inheritance without breaking the language. Schäffer presents an in-depth discussion of how to do so [Sch98]. However, the introduction of multiple inheritance is proprietary and frequently problematic.

As an alternative, developers can use tools to copy method definitions from an abstract class representing a role type to a regular class [RS95]. Such tools can emulate multiple inheritance, but require additional maintenance efforts. However, tool support is better than copying methods by hand, which requires an even higher maintenance effort to keep definitions synchronized.

For structuring a class interface, Smalltalk uses method categories. A method category is a (sub-)set of methods from the overall set of methods of a class. Each class has a set of method categories. A programmer assigns a method to a method category. It is customary that a method is assigned to exactly one method category, but most tools do not enforce this, and it is possible to have a method be an element of more than one method category.

A method category can be used to group all methods of a particular role type. On a design-level, the Figure class provides the role types Figure.Figure, FigureHierarchy.Child, FigureObserver.Subject, Graphics.Client, FigureChain.Predecessor, and ObjectProperty.Provider. On a Smalltalk level, the Figure class provides the corresponding method categories.

The method category GraphicsClient to represent the free no-op role type Graphics.Client is empty, because no methods are assigned to it. It is sensible to make a role type without methods explicit, even if it leads to an empty method category. This is equivalent to annotating the class interface with no-op role types.

It is a Smalltalk convention to have a method category called "Accessing". This method category provides all methods used for simple get and set access to instance variables of an object. From a role modeling perspective, the Accessing method category is problematic. It does not represent a role type but rather cuts across the whole set of role types provided by a class. The Accessing method category provides a good overview of the abstract state of the class, even though it is not guaranteed to give a complete overview.

It does not make sense to break this long-established convention. However, it would be equally annoying, if a method category that represents a role type would be incomplete, because its get and set methods are assigned to the Accessing method category rather than the role type's method category. It is therefore best to keep the methods in both categories, serving both conventions equally well.

Smalltalk does not provide name spaces. Rather, developers use prefixes to tag classes as belonging to a particular name space. This can be used to emulate name spaces. However, it is fairly impractical to have a prefix of several words in front of the actual class name, so that name spaces are only used on a coarse-grained basis.

5.4.6 Extension properties

The following discussion shows how the extension properties asked for in Section 5.2 are realized by the programming language extensions. It thereby describes the rationale behind the extensions.

  • Robustness. All three programming language extensions are robust with respect to being used by developers who do not know about the role modeling extension. The extensions use only such features of the programming language that are widely understood.

    In the case of Smalltalk, a metalevel extension could introduce new role modeling concepts on top of the existing language. Such an extension is not robust, because it requires developers to understand the concepts and handle them appropriately. Section 5.1 discusses the pros and cons of this decision.

  • Inconsistency. A program written in any of the three programming languages may be inconsistent from the point of view of the role modeling extension. However, if it is a proper program, it still runs without problems. Therefore, developers can mix and match role modeling with traditional concepts.
  • Incompleteness. Source code may be inconsistent from a role modeling perspective and therefore incomplete as well.
  • Tool support. There is no dedicated tool support for role modeling in current mainstream programming environments. These environments only support the immediate language features. Therefore, the programmer must maintain a role modeling perspective when implementing designs.

    However, maturing programming environments tend to introduce new tools and concepts that reflect the pragmatics of using programming language constructs better than could be derived from the language definition. we can expect role modeling to provide significant input to these tools.

  • Bridge to design. Annotations provide sufficient means for bridging back from implementation to design, both for developers to look up a particular documentation aspect, and for tools for code generation and round-trip software engineering.

Except for Smalltalk, none of the programming languages provides an extension mechanism that lets us gracefully introduce new language features. As a consequence, we take a conservative approach and use basic language concepts only. Except for the area of tool support, all desirable properties are achieved.

5.5 Summary

This chapter presents the extension of UML, Java, C++, and Smalltalk with the role modeling concepts for framework design. Using these extensions, developers can work in any of these design notations or programming languages and apply the new role modeling concepts where appropriate. The extensions are robust and can cope with inconsistency and incompleteness.

By choosing to provide extensions of industry standards rather than introducing a new isolated approach, Chapter 5 demonstrates that role modeling for framework design is an evolutionary step ahead in the definition of design methods. It adds to existing methods, but does not replace them. It can be used without invalidating existing investments.

This chapter is the last theory chapter. The following chapters are case studies that show how role modeling works in practice.

Copyright (©) 2007 Dirk Riehle. Some rights reserved. (Creative Commons License BY-NC-SA.) Original Web Location: http://www.riehle.org