Structural Composition of Design Patterns
Structural composition approaches build a design by gluing pattern structures that are modeled as class diagrams. Structural composition focuses more on the actual realization of the design rather than abstraction, using different types of models, such as role models. Behavioral composition techniques, such as roles [Reenskaug 1996, Riehle 1997, Kristensen & Osterbye 1996], leave several choices to the designer with less insight on how to continue to the class design phase. Techniques that consider both structural and behavioral views could be complex and difficult to use. Therefore, the POAD approach advocates a structural composition approach with pattern class diagrams. Constructional design patterns in which a pattern interface can be clearly specified (as discussed in Chapter 4), lend themselves to a structural composition approach.
In the following subsections we discuss several structural composition techniques and contrast these techniques with our proposed POAD methodology.We outline an approach for pattern-oriented design proposed by Ram, Anantha, and Guruprasad in A Pattern-Oriented Technique for Software Design (1997). In contrast to our top-down approach, this approach describes a bottom-up process to design software using design patterns. The approach shows how related patterns can be selected; however, it does not clearly show how patterns can be composed. Nevertheless, it gives an example of previous attempts in the literature to develop a systematic process for pattern-oriented software development.
We briefly introduce the concept of software design using design components that can adapt to rapidly changing requirements as proposed by Keller and Schauer in Design Components: Towards Software Composition at the Design Level (1998). This work is presented to highlight the importance of the concept of generic design components, where the components are specified in a high level of abstraction. This concept is very much related to the notion of using design patterns as design components. The concept is also different from the concepts of component-based software development, which mainly focus implementation or code-level components as depicted, for example, by component diagrams in UML. Keller and Schauer's work shows how generic design components can evolve to domain-specific design components from which we can obtain code-level components. This approach insures that design quality translates into code quality.
We briefly discuss another approach in the literature on the development of component-based frameworks using patterns. In this work a hierarchical architectural design is developed in which a component can implement many patterns and frameworks, and a pattern or a framework can be implemented across many components. The work takes a structural approach to glue patterns and uses UML interface classes to model interfaces of patterns. UML component diagrams are then used to represent the framework as a composition of physical components that do not have a one-to-one mapping with patterns. We briefly discuss the difference between this work and the POAD approach for pattern-oriented development.
The Catalysis approach created by D'Souza and Wills and discussed in Catalysis: Component and Framework-based Development (1998). The approach defines a component-based approach to software development that is heavily based on interfaces at both the design and implementation levels. The Catalysis approach is used to build object- and component-based systems using UML and extensions of UML.
Finally we briefly introduce the newly developed concepts of composition patterns and subject-oriented design [Clarke & Walker 2001; Clarke et al. 2000]. These concepts show an emerging development technology based on the concept of aspect-oriented programming.
A Pattern-Oriented Composition Technique
We start by outlining a structural composition approach that follows traditional OO analysis and design methodologies. Ram, Anantha, and Guruprasad, in A Pattern-Oriented Technique for Software Design (1997), describe a bottom-up pattern-oriented approach to design software, Pattern Oriented Technique (POT). They start by analyzing the application in a traditional OO manner. Patterns are then identified and classes are grouped. This approach is not a pattern-oriented development process; it is a traditional OO development process that is augmented with a refactoring technique based on patterns. The steps of the process proposed by POT are summarized as follows:
Identify classes: The classes are identified based on the requirements specifications using traditional OO techniques.
Identify responsibilities: Class services are identified to define the behavior of each class. Traditional OO techniques are used to define the service methods in each class.
Identify interacting classes: Interaction diagrams are developed to define in an abstract sense (i.e., using association or dependency relationships) the interacting classes in what is called class interaction diagrams. Following traditional OO techniques and using UML, collaboration diagrams or sequence diagrams can be used to identify interacting objects that will reflect on relating the classes of these objects in the class diagram using association or dependency relationships.
Identify class groups: Ram and his colleagues define a class group as a collection of classes such that each class in the group interacts with at least one other class in the group. The number of classes in the group determines the size of the group, which is at least one. These groups can be overlapping; that is, a class may belong to more than one group; hence, a large number of groups is possible.
Identify class group interactions: The interactions between classes in each class group are identified in terms of service functions. This step would use sequence diagrams in UML to identify the interactions between classes in each class group.
Specify class group interactions at an abstract level: Interactions between classes in each group are specified in terms of objects, lists of objects, or commands sent from one object to another. For example, an object of a class in a group sends a command to another object of another class in the group, or an object maintains and iterates on a list of objects of another type in the same group. This step is specified to make the group interactions closer to the abstract description of known patterns.
Identify design patterns: In this step, the intents of design patterns are compared with class group interactions in each group to identify relevant patterns. It is not clear how this comparison should take place, since the intents are usually specified in a textual form, while class group interactions are specified in terms of interactions between objects, as mentioned above. In fact, due to the potentially large number of class groups, this step is very tedious and time consuming. The analysis phase of the POAD process, described in Chapter 8, addresses this problem of selecting the set of related patterns based on the structural and behavioral UML analysis diagrams.
Obtain rough designs: The design patterns identified in the previous step form a set of rough designs for the class groups. Class diagram refinements are introduced based on instantiations of these patterns.
Evaluate design tradeoffs: Quantitative measures based on four tradeoffs are derived to assess the tradeoffs of using patterns. These are briefly summarized as follows:
Coding efforts is a measure of the amount of coding needed for implementing the pattern.
Static adaptability is a measure of the adaptability of the pattern to a particular context during implementation.
Dynamic adaptability is a measure of adapting the pattern behavior at run time.
Performance is a measure of the runtime performance of the pattern in terms of the response time needed to deliver the services expected.
Develop a detailed design: The patterns are instantiated and glued together by defining the relationships between classes in different patterns. This step in fact is not trivial and is not clearly specified.
The main drawback of this process is that patterns are used late in the process after application classes have been defined. In POT, patterns are used as a refactoring technique. In Chapter 7 we describe a systematic development process for selecting and using patterns in a top-down fashion.
Software Composition at the Design Level Using Design Components
Component-based software development has become the focus of many recent design and implementation methodologies. Keller and Schauer address the problem of software composition at the design level using design components [Keller & Schauer 1998, Schauer & Keller 1998]. Their project is called Software desirable Properties into the design of Object-Oriented Large-scale software systems (SPOOL). The SPOOL project was organized by CSER (Consortium for Software Engineering Research), which is funded by Bell Canada, NSERC (National Sciences and Research Council of Canada), and NRC (National Research Council of Canada). The project focuses on the problem of adapting software systems in large telecommunication companies due to rapidly changing requirements. The approach is similar to the POAD approach in creating a design based on well-defined and proven design patterns packaged into tangible, customizable, and composable design components.
Keller defines the evolution of a pattern in a design composition environment along four dimensions:
Concreteness, which defines how the design component evolves from abstraction to concreteness.
Specificity, which defines how the design component evolves from being generic to domain-specific.
Scope, which defines how the component evolves from being a basic component to a more composite form where its internals become of relevance.
Revision, which defines the versions of the component from its basic form to its current form.
Keller also identifies the limitations of UML modeling of a design component. Components diagrams in UML are used to define code-level components. Patterns in UML are defined as dotted ellipses (collaborations). Keller suggests an extension to UML package diagrams to model patterns and their instantiations.
The structural composition approach proposed by Keller does not define a design process to compose patterns together and does not define a notion for pattern interfaces. Keller uses a package diagram to represent a design pattern as a component. We define visual models that are more expressive because they are based on interfaces. Package representation has several syntax and semantic limitations, as discussed in Chapter 5. The approach in Keller's work (1998), though concerned with a problem similar to ours, focuses on what needs to be placed on a pattern as a design component and little about the procedure of composing components.
Another major concern about this approach is that it integrates two patterns to form a part of the design, and then it integrates another pattern to the design, and so on. This approach loses some benefits of a design component. First, the abstract view of the design is lost; there is no visual view to reflect the high-level structure of the system in terms of patterns. Second, the approach does not use interfaces, which is expected by the user of a component to glue pattern structures together. The pattern visualization tool supporting Keller's approach does not support distinct pattern views that are traceable to class diagrams. Instead, the tool supports covering a class diagram with boundaries on pattern participants. Such technique, also used in Odenthal and Quibeldey-Cirkel's Using Patterns for Design and Documentation (1997), is in our view cumbersome and does not support high-level view of the design.
Component-Based Frameworks Using Patterns
The development of application frameworks is now an important topic in modern design and implementation techniques. Larsen (1999) takes a structural approach to glue patterns by mapping the roles and participants of a pattern into actual model elements that fulfill these roles. Larsen uses UML models to glue together components. A component can implement many patterns and frameworks, and a pattern or a framework can be implemented across many components. In his definitions, a framework contains more than one pattern and has several extension points for different applications. In fact, the pattern and framework metamodel shown by Larsen exhibits the Composite pattern, where the pattern is the base class and the framework is the composite derived class with extensions, which is composed of one or more objects of type pattern.
The focus of Larsen's work is designing and delivering frameworks using the concepts of patterns at the design phase and using components at the delivery and implementation phase. In this case, a framework or a pattern can be implemented by several components.
At the design phase of the framework, Larsen uses UML interface classes to model interfaces for patterns. The solution of a pattern as a class diagram is augmented with additional classes as interfaces. These class diagrams of patterns (with additional interface classes) are used to define the framework in one class diagram model. At the delivery phase of a framework, Larsen uses UML component diagrams to represent the framework a as composition of physical components that are not a one-to-one mapping to patterns.
The POAD approach shares the same concept of defining interfaces for patterns. However, Larsen's approach is different from POAD in several aspects:
Larsen uses class diagrams to represent the composition of patterns' classes. His example shows a class diagram for each pattern (with their interface classes added) and then a class diagram for the framework. POAD uses hierarchical logical models that hide details that are not utilized directly at a given design-level abstraction. We use additional models for patterns with no interfaces or internal details, then another layer showing interfaces and their associations, and finally the detailed layer showing details and interfaces. Larsen's approach lacks the first two layers.
Larsen uses component diagrams for delivering the framework and not for design. This definition complies with the UML definition of a physical component. Instead, POAD uses patterns as logical design components (similar to packages) rather than physical deliverable components.
Larsen's approach does not support hierarchical views of the design. In POAD we use a pattern as a black box and then zoom into internal details. Larsen uses class diagrams only.
The traceability problem is not solved in Larsen's approach. Pattern participants are lost at the framework design diagrams. POAD models can trace pattern participants' bottom-up and top-down as discussed later in the book.
The Catalysis Approach
The design of components interfaces is one of the most important steps in the architectural design process. D'Souza and Wills (1998) define a component-based approach to develop software that is heavily based on interfaces at both the design and implementation level. The approach is called Catalysis, which is used to build object- and component-based systems using UML and some extensions made to UML constructs. At the design phase, D'Souza advocates using frameworks as building components, where a framework is a pattern of model or code that can be applied to different problems and OO frameworks are collaborations with a default, skeletal implementation. We take a similar approach for designing software as a composition of constructional design patterns; however, we distinguish patterns from frameworks. POAD addresses a more specific problem (composition of patterns) and can be used to build such frameworks. Catalysis addresses other issues in developing software, such as composing physical components, distribution of components, and business driven solutions.
Catalysis uses three modeling concepts from UML in order to define components and component interfaces. These modeling concepts are described briefly as follows:
Types. A type models component interfaces by defining the visible behavior of objects. It uses the class construct in UML to specify the attributes and operations of a type. Catalysis clearly distinguishes a type from a class as modeling concepts, since a type specifies only type abstraction, while a class also specifies implementations. The example of this distinction, given in the constructs of a programming language such as Java, is the distinction between an interface construct of Java that corresponds to the type abstraction defined here and the class in Java that implements an interface and hence must include implementations of operations declared in the interface.
Collaborations. Similar to the UML terminology, collaboration defines how interactions between objects or components (i.e., a group of objects) take place using types and actions. Actions are specific invocations of operations defined in a type (i.e., an interface) of an object or a component invoked by a collaborating object or component. The methodology in D'Souza's work treats collaborations as design units. Each design unit defines a design for certain services specified in the system requirements. Collaborations can be composed to define a more complex service or behavior. The collaboration-modeling concept defines the architecture of the application, where every collaboration defines an element of the architecture. This concept of architecture definition is inconsistent with the UML modeling view of collaborations as dynamic realizations of use cases, whereas an architecture is a static modeling view of the components and their interfaces.
Refinement. The third modeling concept used in Catalysis is defining relationships between elements of the other two modeling concepts, that is, between types, between collaborations, or between a type and a class. This concept is based on a relationship between an abstraction and a realization—for example, a type and a class. This modeling concept is very vague and ambiguous, specifically when it applies to collaborations and types. The clarification comes from the concept of specifying collaborations to types in an abstract sense and then defines refinements of these elements as realizations or more detailed specifications of these abstractions; hence, different levels of abstractions can be defined. The UML packages are used to separate these different levels of abstractions. The objective is to permit the reuse of a given abstraction by multiple realizations. A package can be defined as a group of types, collaborations, actions, and even code components.
The modeling concepts described above show that the Catalysis approach is basically a component-based development approach that does not depend on the concept of design patterns as we use in POAD. The above modeling concepts clearly show the difficulty in developing a design using the Catalysis methodology. There is no clear distinction between the dynamic view and the static view. There is no clear process for developing the design artifacts. Patterns and frameworks are mentioned as reusable abstractions, but no clear techniques are specified on how to integrate these abstractions in the design process.
Composition Patterns, Subject-Oriented Programming, and Aspect-Oriented Software Development
Clarke and Walker have introduced the concept of composition patterns recently in Composition Patterns: An Approach to Designing Reusable Aspects (2001) to support designs developed using the Subject-Oriented Design (SOD) model. The SOD model introduced in Clarke, Harrison, Ossher, and Tarr's Subject-Oriented Design (2000) has evolved from subject-oriented programming, in which different subjects may be developed and coded to support separate requirements. These requirements, also called aspects, can be overlapping functional or crosscutting requirements. Aspects, just like objects, can be defined at any stage of the software lifecycle, including requirements specification, design, and implementation.
Generally, crosscutting aspects can be defined from design or architectural constraints, or common properties or behaviors, and features. The SOD model is based on the concepts of Aspect-Oriented Software Development (AOSD) evolving from aspect-oriented programming. AOSD is a new technology developed to support the concept of separation of concerns (SOC) in software development. The technology is used to produce modular crosscutting aspects of a system. Examples of crosscutting aspects include logging or tracing of activities, error recovery, and synchronization of activities. The work in AOSD is mainly driven by the fundamental goal of better SOC. Although crosscutting requirements tend to be a significant focus of this technology, the published work [Erlad et al. 2001] also incorporates other kinds of SOC techniques, including the other well-established approaches for developing modules such as OO design and structured design. Hence, AOSD blends support for many different types of modular designs, including block structure, object structure, inheritance, and crosscutting.
In a workshop entitled Advanced Separation of Concerns (ASoC) held at the 2001 European Conference on Object-Oriented Programming (ECOOP2001), and in similar workshops in other major conferences in this field, the presentations clearly show how the SOC concept is providing an added value in addressing difficult requirements in OO development in general and in the problem of design composition in particular.
Crosscutting requirements such as tracing, error recovery, and synchronization are requirements that have impact on and are scattered across multiple classes in a system. The tracing requirement, for example, which involves tracing the entry and exit of selected class operations during runtime, could impact many operations of many classes in the system. Such requirements pose a challenge for developing reusable and maintainable OO designs. The SOD model addresses these requirements by decomposing the design into different design models called design subjects. Each design subject addresses the design of a crosscutting requirement. Composing these design subjects with the rest of the system, however, can also become a difficult and error-prone task.
Clarke and Walker (2001) introduced the concept of composition patterns as a way to develop reusable design patterns for these crosscutting requirements. The composition design pattern addresses the problem of composing the potentially overlapping design subjects with the rest of the system. The concept is based on UML templates, where a UML class diagram–stereotype parameterized package is used to define a reusable design subject. The parameters of this template-stereotyped package can be a list of class names followed by operations or operation signatures of these classes involved in the design subject.
For example, consider the Trace design subject composition pattern [Clarke & Walker 2001]. The Trace pattern is specified as a template package with two parameters, the TracedClass parameter and the _tracedOp() parameter. This template package (or composition pattern) is instantiated by binding it to a design subject called Application package containing Classi with the operation fun() to be traced (see Figure 3-7). This binding produces a design subject called TracedApplication showing a composition between the Trace design subject and the Application design subject containing the operation fun() to be traced (see Figure 3-8).
Figure 3-7. The Trace pattern template package and its binding to an Application package [Clarke & Walker 2001].
Figure 3-8. The output of binding the Trace composition pattern with the Application package [Clarke & Walker 2001].
Figure 3-7 shows the Trace design subject template package containing class Trace and class TracedClass, and shows the binding of this package with the Application design subject package. This binding implies that Classi and its operation fun() in Application will replace the TracedClass and _tracedOp(..) specified in the template parameters. This can be expanded to other classes or functions in Application by adding a list of statements to the bind note inside the square brackets in Figure 3-7.
In the TracedApplication design subject in Figure 3-8 the TracedClass and tracedOp(..) were replaced by Classi and the method fun() respectively based on the bind statement shown in Figure 3-7. The method Application_fun() in Classi of the TracedApplication design subject contains in fact the original body of method fun() as specified in the Application design subject, whereas the body of method fun() in the TracedApplication design subject would also contain a call to the traceEntry(String), followed by a call to Application_fun(), and then followed by a call to traceExit(String) method. This behavior is specified in the sequence diagram in Figure 3-8. The sequence diagram shows an interaction model of the behavior of method fun() in the TracedApplication design subject. The parameter of methods traceEntry() and traceExit() is a string containing the name of the entered method. This sequence diagram is the way to specify crosscutting behavior in SOD. It identifies the methods specified in different design subjects that correspond and should be merged. In this simple example, the operation Application_fun() of Classi in the TracedApplication design subject should merge with operation fun() of Classi in the Application design subject. This merge implies that each reference to Application_fun() will be replaced by a reference to the method fun() of Classi in the Application design subject.
The trace example described above can be considered as instantiating a Trace design pattern on an application design. The work of Clarke and Walker in (2001) shows examples of other design patterns, such as the Synchronization pattern in which several functions of a class accessing a shared object need to be synchronized. The Synchronization pattern template package is applied to a design example of a library management application. The Observer pattern template package is also applied to the same example.
The approach described above, as far as pattern-oriented design is concerned, is based on applying design pattern templates to existing designs. Hence, it differs from the POAD methodology, which advocates the notion of developing application designs from scratch using patterns as building blocks.
The use of template packages in this approach is in fact very convenient in terms of specifying the changes to be done when applying a design pattern modeled as a design subject to an application design subject where the pattern can be used. In this case it produces the details of a composition or a single instantiation of the design pattern in the evolving design. This approach in fact can be used in the design refinement phase of the POAD process where domain-specific Detailed Pattern-Level diagrams are developed.