0% found this document useful (0 votes)
16 views

Unit 4

This document discusses object design concepts related to reuse including application objects, solution objects, specification inheritance, implementation inheritance, delegation, the Liskov substitution principle, and how these concepts are used in design patterns. It also discusses how object design activities are iterative and covers in multiple chapters.

Uploaded by

Harini
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
16 views

Unit 4

This document discusses object design concepts related to reuse including application objects, solution objects, specification inheritance, implementation inheritance, delegation, the Liskov substitution principle, and how these concepts are used in design patterns. It also discusses how object design activities are iterative and covers in multiple chapters.

Uploaded by

Harini
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 26

UNIT IV

OBJECT DESIGN AND IMPLEMENTATION ISSUES


Reusing Pattern Solutions
An Overview of Object Design Conceptually, software system development
fills the gap between a given problem and an existing machine. The
activities of system development incrementally close this gap by identifying
and defining objects that realize part of the system (Figure 8-1). Analysis
reduces the gap between the problem and the machine by identifying
objects representing problem-specific concepts. During analysis the system
is described in terms of external behavior such as its functionality (use case
model), the application domain concepts it manipulates (object model), its
behavior in terms of interactions (dynamic model), and its nonfunctional
requirements. System design reduces the gap between the problem and
the machine in two ways. First, system design results in a virtual machine
that provides a higher level of abstraction than the machine. This is done
by selecting off-the-shelf components for standard services such as
middleware, user interface toolkits, application frameworks, and class
libraries. Second, system design identifies off-the-shelf components for
application domain objects such as reusable class libraries of banking
objects. After several iterations of analysis and system design, the
developers are usually left with a puzzle that has a few pieces missing.
These pieces are found during object design. This includes identifying new
solution objects, adjusting off-the-shelf components, and precisely
specifying each subsystem interface and class. The object design model
can then be partitioned into sets of classes that can be implemented by
individual developers.
 Reuse. Off-the-shelf components identified during system design are
used to help in the realization of each subsystem. Class libraries and
additional components are selected for basic data structures and
services. Design patterns are selected for solving common problems
and for protecting specific classes from future change. Often,
components and design patterns need to be adapted before they can
be used. This is done by wrapping custom objects around them or by
refining them using inheritance. During all these activities, the
developers are faced with the same buy-versus-build trade-offs they
encountered during system design.

 Interface specification. During this activity, the subsystem services


identified during system design are specified in terms of class
interfaces, including operations, arguments, type signatures, and
exceptions. Additional operations and objects needed to transfer data
among subsystems are also identified. The result of service
specification is a complete interface specification for each subsystem.
The subsystem service specification is often called subsystem API
(Application Programmer Interface).

 Restructuring. Restructuring activities manipulate the system model


to increase code reuse or meet other design goals. Each
restructuring activity can be seen as a graph transformation on
subsets of a particular model. Typical activities include transforming
N-ary associations into binary associations, implementing binary
associations as references, merging two similar classes from two
different subsystems into a single class, collapsing classes with no
significant behavior into attributes, splitting complex classes into
simpler ones, and/or rearranging classes and operations to increase
the inheritance and packaging. During restructuring, we address
design goals such as maintainability, readability, and
understandability of the system model.

 Optimization. Optimization activities address performance


requirements of the system model. This includes changing algorithms
to respond to speed or memory requirements, reducing multiplicities
in associations to speed up queries, adding redundant associations
for efficiency, rearranging execution orders, adding derived attributes
to improve the access time to objects, and opening up the
architecture, that is, adding access to lower layers because of
performance requirements. Object design is not sequential. Although
each group of activities described above addresses a specific object
design issue, they usually occur concurrently. A specific off-the-shelf
component may constrain the number of types of exceptions
mentioned in the specification of an operation and thus may impact
the subsystem interface. The selection of a component may reduce
the implementation work while introducing new “glue” objects, which
also need to be specified. Finally, restructuring and optimizing may
reduce the number of components to be implemented by increasing
the amount of reuse in the system. Usually, interface specification
and reuse activities occur first, yielding an object design model that is
then checked against the use cases that exercise the specific
subsystem. Restructuring and optimization activities occur next, once
the object design model for the subsystem is relatively stable.
Focusing on interfaces, components, and design patterns results in
an object design model that is much easier to modify. Focusing on
optimizations first tends to produce object design models that are
rigid and difficult to modify. However, as depicted in Figure 8-2,
activities of object design occur iteratively. Given the variety and
breadth of activities in object design, we divided this material into
three different chapters. This chapter focuses on activities related to
reuse, in particular, components and design patterns. In the next
chapter, Chapter 9, Object Design: Specifying Interfaces, we examine
the activities related to interface specification, in particular, UML’s
Object Constraint Language and its use for specifying invariants,
preconditions, and post conditions. In Chapter 10, Mapping Models to
Code, we examine the activities related to restructuring and
optimization.
Reuse Concepts: Solution Objects, Inheritance, and Design Patterns
In this section, we present the object design concepts related to reuse:
• Application Objects and Solution Objects
• Specification Inheritance and Implementation Inheritance
• Delegation
• The Liskov Substitution Principle
• Delegation and Inheritance in Design Patterns
Application Objects and Solution Objects
As we saw in Chapter 2, Modeling with UML, class diagrams can be used
to model both the application domain and the solution domain. Application
objects, also called “domain objects,” represent concepts of the domain
that are relevant to the system. Solution objects represent components that
do not have a counterpart in the application domain, such as persistent
data stores, user interface objects, or middleware.
During analysis, we identify entity objects and their relationships, attributes,
and operations. Most entity objects are application objects that are
independent of any specific system. During analysis, we also identify
solution objects that are visible to the user, such as boundary and control
objects representing forms and transactions defined by the system. During
system design, we identify more solution objects in terms of software and
hardware platforms. During object design, we refine and detail both
application and solution objects and identify additional solution objects
needed to bridge the object design gap.
Specification Inheritance and Implementation Inheritance
During analysis, we use inheritance to classify objects into taxonomies.
This allows us to differentiate the common behavior of the general case,
that is, the superclass (also called the “base class”), from the behavior that
is specific to specialized objects, that is, the subclasses (also called the
“derived classes”). The focus of generalization (i.e., identifying a common
superclass from a number of existing classes) and specialization (i.e.,
identifying new subclasses given an existing superclass) is to organize
analysis objects into an understandable hierarchy. Readers of the analysis
model can start from the abstract concepts, grasp the core functionality of
the system, and make their way down to concrete concepts and review
specialized behavior. For example, when examining the analysis model for
the FRIEND emergency response system described in Chapter 4,
Requirements Elicitation, we first focus on understanding how the system
deals with Incidents in general, and then move to the differences in
handling Traffic Accidents or Fires. The focus of inheritance during object
design is to reduce redundancy and enhance extensibility. By factoring all
redundant behavior into a single superclass, we reduce the risk of
introducing inconsistencies during changes (e.g., when repairing a defect)
since we have to make changes only once for all subclasses. By providing
abstract classes and interfaces that are used by the application, we can
write new specialized behavior by writing new subclasses that comply with
the abstract interfaces. For example, we can write an application
manipulating images in terms of an abstract Image class, which defines all
the operations that all Images should support, and a series of specialized
classes for each image format supported by the application (e.g.,
GIFImage, JPEGImage). When we need to extend the application to a new
format, we only need to add a new specialized class.
Although inheritance can make an analysis model more understandable
and an object design model more modifiable or extensible, these benefits
do not occur automatically. On the contrary, inheritance is such a powerful
mechanism that novice developers often produce code that is more
obfuscated and more brittle than if they had not used inheritance in the first
place.
Consider the following example: Assume for a moment that Java does not
provide a set abstraction and that we needed to write our own. We decide
to reuse the java.util.Hashtable class to implement a set abstraction that we
call MySet. Inserting an element in MySet is equivalent to checking if the
corresponding key exists in the table and creating an entry if necessary.
Checking if an element is in MySet is equivalent to checking if an entry is
associated with the corresponding key (see Figure 8-3, left column)
Such an implementation of a set allows us to reuse code and provides us
with the desired behavior. It also provides us, however, with unwanted
behavior. For example, Hashtable implements the containsKey() operation
to check if the specified object exists as a key in the Hashtable and the
containsValue() operation to check if the specified object exists as an entry.
containsKey() is inherited by MySet, but containsValue() is overwritten.
Given our implementation, the operation containsValue() invoked on a
MySet object has the same behavior as containsKey(), which is
counterintuitive. Worse, a developer could use both containsKey() and
containsValue(), which would make it difficult to change the internal
representation of MySet in the future. For example, if we decided to
implement MySet as a List instead of a Hashtable, all invocations to
containsKey() would become invalid. To address this issue, we could
overwrite all operations inherited from Hashtable that should not be used
on MySet with methods throwing exceptions. However, this would lead to a
MySet class that is difficult to understand and reuse.
Inheritance yields its benefits by decoupling the classes using a superclass
from the specialized subclasses. In doing so, however, it introduces a
strong coupling along the inheritance hierarchy between the superclass
and the subclass. Whereas this is acceptable when the inheritance
hierarchy represents a taxonomy (e.g., it is acceptable for Image and
GIFImage to be tightly coupled), it introduces unwanted coupling in the
other cases. In our example, two previously unrelated concepts, Hashtable
and Set, become tightly coupled as a result of subclassing, introducing
many issues when Hashtable is modified or when a Set is used by a class
as a specialized Hashtable. The fundamental problem in this example is
that, although Hashtable provides behavior that we would like to reuse in
implementing Set, because that would save us time, there is no taxonomy
in which the Set concept is related to the Hashtable concept
Figure 8-3 An example of implementation inheritance. The left column
depicts a questionable implementation of MySet using implementation
inheritance. The right column depicts an improved implementation using
delegation (UML class diagram and Java).

The use of inheritance for the sole purpose of reusing code is called
implementation inheritance. With implementation inheritance, developers
reuse code quickly by subclassing an existing class and refining its
behavior. A Set implemented by inheriting from a Hashtable is an example
of implementation inheritance. Conversely, the classification of concepts
into type hierarchies is called specification inheritance (also called
“interface inheritance”). The UML class model of Figure 8-4 summarizes
the four different types of inheritance we discussed in this section.
Figure 8-4 Inheritance meta-model (UML class diagram). In object-oriented
analysis and design, inheritance is used for achieving several goals, in
particular modeling taxonomies and reusing behavior from abstract
classes. When modeling taxonomies, the inheritance relationships can be
identified either during specializations (when specialized classes are
identified after general ones) or during generalizations (when general
classes are abstracted out of a number of specialized ones). When using
inheritance for reuse, specification inheritance represents subtyping
relationships, and implementation inheritance represents reuse among
conceptually unrelated classes.
Delegation
Delegation is the alternative to implementation inheritance that should be
used when reuse is desired. A class is said to delegate to another class if it
implements an operation by resending a message to another class.
Delegation makes explicit the dependencies between the reused class and
the new class. The right column of Figure 8-3 shows an implementation of
MySet using delegation instead of implementation inheritance. The only
significant change is the private field table and its initialization in the
MySet() constructor. This addresses both problems we mentioned before:
• Extensibility. The MySet on the right column does not include the
containsKey() method in its interface and the new field table is private.
Hence, we can change the internal representation of MySet to another
class (e.g., a List) without impacting any clients of MySet.
• Subtyping. MySet does not inherit from Hashtable and, hence, cannot be
substituted for a Hashtable in any of the client code. Consequently, any
code previously using Hashtables still behaves the same way
Delegation is a preferable mechanism to implementation inheritance as it
does not interfere with existing components and leads to more robust code.
Note that specification inheritance is preferable to delegation in subtyping
situations as it leads to a more extensible design.
The Liskov Substitution Principle
The Liskov Substitution Principle [Liskov, 1988] provides a formal definition
for specification inheritance. It essentially states that, if a client code uses
the methods provided by a superclass, then developers should be able to
add new subclasses without having to change the client code. For
example, in the left column of Figure 8-3, this means that, if a client uses a
Hashtable, the client should not have to be modified when we substitute the
Hashtable for any of its subclasses, for example MySet. Clearly, this is not
the case, so the relationship between MySet and Hashtable is not a
specification inheritance relationship. Below is the formal definition of the
Liskov Substitution Principle.
Liskov Substitution Principle
If an object of type S can be substituted in all the places where an object of
type T is expected, then S is a subtype of T.
Interpretation
In object design, the Liskov Substitution Principle means that if all classes
are subtypes of their superclasses, all inheritance relationships are
specification inheritance relationships. In other words, a method written in
terms of a superclass T must be able to use instances of any subclass of T
without knowing whether the instances are of a subclass. Consequently,
new subclasses of T can be added without modifying the methods of T,
hence leading to an extensible system. An inheritance relationship that
complies with the Liskov Substitution Principle is called strict inheritance.

Delegation and Inheritance in Design Patterns


In general, when to use delegation or inheritance is not always clear and
requires some experience and judgement on the part of the developer.
Inheritance and delegation, used in different combinations, can solve a
wide range of problems: decoupling abstract interfaces from their
implementation, wrapping around legacy code, and/or decoupling classes
that specify a policy from classes that provide mechanism. In object-
oriented development, design patterns are template solutions that
developers have refined over time to solve a range of recurring problems
[Gamma et al., 1994]. A design pattern has four elements
1. A name that uniquely identifies the pattern from other patterns.
2. A problem description that describes the situations in which the pattern
can be used. Problems addressed by design patterns are usually the
realization of modifiability and extensibility design goals and nonfunctional
requirements.
3. A solution stated as a set of collaborating classes and interfaces.
4. A set of consequences that describes the trade-offs and alternatives to
be considered with respect to the design goals being addressed.
For example, we can restate the problem of writing a set class of Figure 8-3
as implementing a new class (i.e., MySet) that complies with an existing
interface (i.e., the Java Set interface) reusing the behavior provided by an
existing class (i.e., the Hashtable class). Both the Set interface and the
Hashtable class are already provided and neither can be modified. The
Adapter design pattern (Figure 8-5; Appendix A.2) is a template solution for
such problems.
The Adapter pattern works as follows: An Adapter class implements each
method declared in the ClientInterface in terms of requests to the
LegacyClass. Any conversion of data structures or adjustment of behaviors
is done in the Adapter class so that Adapter behaves as expected by the
Client. The Adapter pattern enables reuse since neither the ClientInterface
nor the LegacyClass need to be modified. The Adapter pattern also
encourages extensibility, as the same Adapter class can be used for any
subtypes of the LegacyClass, as subtypes can be substituted for their
supertype, according to the Liskov Substitution Principle. By applying the
Adapter pattern to our Set problem (Figure 8-6), we end up with the same
delegation relationship between MySet and Hashtable as in Figure 8-3.
Note that the Adapter pattern uses both inheritance and delegation. When
studying design patterns, you will notice that many patterns use a mix of
inheritance and delegation and therefore look similar. However, the same
mechanisms are used in subtly different ways. To clarify the differences,
we use the following terms to denote different classes participating in the
pattern:
• The client class accesses the pattern. In the class diagram of the Adapter
pattern (Figure 8-5), this class is simply called Client. Client classes can be
either existing classes of a class library or new classes of the system under
development.
• The pattern interface is the part of the pattern that is visible to the client
class. Often, the pattern interface is realized by an abstract class or an
interface. In the Adapter pattern, this class is called Client Interface.
• The implementor class provides the lower-level behavior of the pattern. In
the Adapter pattern, the LegacyClass and the Adapter are implementor
classes. In many patterns, a number of collaborating implementor classes
are needed to realize the pattern behavior.
• The extender class specializes an implementor class to provide a different
implementation or an extended behavior of the pattern. In the Adapter
pattern, the subtypes of LegacyClass are extender classes. Note that,
often, extender classes represent future classes that developers anticipate.
Since developers have strived to evolve and refine design patterns for
maximizing reuse and flexibility, they are usually not solutions that
programmers would initially think of. As design patterns capture a great
deal of knowledge (e.g., by documenting the context and tradeoffs involved
in applying a pattern), they also constitute a source of guidance about
when to use inheritance and delegation.
In the next section, we examine the use of design patterns and frameworks
for solving a range of common object design problems.
Specifying Interfaces
During object design, we identify and refine solution objects to realize the subsystems defined during
system design. During this activity, our understanding of each object deepens: we specify the type
signatures and the visibility of each of the operations, and, finally, we describe the conditions under
which an operation can be invoked and those under which the operation raises an exception. As the
focus of system design was on identifying large chunks of work that could be assigned to individual
teams or developers, the focus of object design is on specifying the boundaries between objects. At this
stage in the project, a large number of developers concurrently refines and changes many objects and
their interfaces. The pressure to deliver is increasing and the opportunity to introduce new, complex
faults into the design is still there. The focus of interface specification is for developers to communicate
clearly and precisely about increasingly lower-level details of the system. The interface specification
activities of object design include

• identifying missing attributes and operations

• specifying type signatures and visibility

• specifying invariants

• specifying preconditions and postconditions.

In this chapter, we provide an overview of the concepts of interface specification. We introduce OCL
(Object Constraint Language) as a language for specifying invariants, preconditions, and postconditions.
We discuss heuristics and stylistic guidelines for writing readable constraints. Finally, we examine the
issues related to documenting and managing interface specifications.

Overview of Interface Specification:

At this point in system development, we have made many decisions about the system and produced a
wealth of models:

• The analysis object model describes the entity, boundary, and control objects that are visible to the
user. The analysis object model includes attributes and operations for each object.

• Subsystem decomposition describes how these objects are partitioned into cohesive pieces that are
realized by different teams of developers. Each subsystem includes highlevel service descriptions that
indicate which functionality it provides to the others.

• Hardware/software mapping identifies the components that make up the virtual machine on which we
build solution objects. This may include classes and APIs defined by existing components.

• Boundary use cases describe, from the user’s point of view, administrative and exceptional cases that
the system handles.

• Design patterns selected during object design reuse describe partial object design models addressing
specific design issues.

All these models, however, reflect only a partial view of the system. Many puzzle pieces are still missing
and many others are yet to be refined. The goal of object design is to produce an object design model
that integrates all of the above information into a coherent and precise whole. The goal of interface
specification, the focus of this chapter, is to describe the interface of each object precisely enough so
that objects realized by individual developers fit together with minimal integration issues. To this end,
interface specification includes the following activities:
 Identify missing attributes and operations. During this activity, we examine each subsystem
service and each analysis object. We identify missing operations and attributes that are needed
to realize the subsystem service. We refine the current object design model and augment it with
these operations.
 Specify visibility and signatures. During this activity, we decide which operations are available to
other objects and subsystems, and which are used only within a subsystem. We also specify the
return type of each operation as well as the number and type of its parameters. This goal of this
activity is to reduce coupling among subsystems and provide a small and simple interface that
can be understood easily by a single developer.
 Specify contracts. During this activity, we describe in terms of constraints the behavior of the
operations provided by each object. In particular, for each operation, we describe the conditions
that must be met before the operation is invoked and a specification of the result after the
operation returns.
The large number of objects and developers, the high rate of change, and the concurrent
number of decisions made during object design make object design much more complex than
analysis or system design. This represents a management challenge, as many important
decisions tend to be resolved independently and are not communicated to the rest of the
project. Object design requires much information to be made available among the developers so
that decisions can be made consistent with decisions made by other developers and consistent
with design goals. The Object Design Document, a live document describing the specification of
each class, supports this information exchange.

Interface Specification Concepts


In this section, we present the principal concepts of interface specification:
• Class Implementor, Class Extender, and Class User
• Types, Signatures, and Visibility
• Contracts: Invariants, Preconditions, and Postconditions
• Object Constraint Language
• OCL Collections: Sets, Bags, and Sequences
• OCL Qualifiers: forAll and exists

Class Implementor, Class Extender, and Class User


So far, we have treated all developers as equal. Now that we are delving into the details of
object design and implementation, we need to differentiate developers based on their point of
view. While all use the interface specification to communicate about the class of interest, they
view the specifications from radically different point of views :
• The class implementor is responsible for realizing the class under consideration. Class
implementors design the internal data structures and implement the code for each public
operation. For them, the interface specification is a work assignment.
• The class user invokes the operations provided by the class under consideration during the
realization of another class, called the client class. For class users, the interface specification
discloses the boundary of the class in terms of the services it provides and the assumptions it
makes about the client class.
• The class extender develops specializations of the class under consideration. Like class
implementors, class extenders may invoke operations provided by the class of interest, the class
extenders focus on specialized versions of the same services. For them, the interface
specification both a specifies the current behavior of the class and any constraints on the
services provided by the specialized class

For example, consider the ARENA Game abstract class (Figure 9-2). The developer responsible for
realizing the Game class, including operations that apply to all Games, is a class implementor. The
League and Tournament classes invoke operations provided by the Game interface to organize and start
Matches. Developers responsible for League and Tournament are class users of Game. The TicTacToe
and Chess classes are concrete Games that provide specialized extensions to the Game class. Developers
responsible for those classes are class extenders of Game.

Types, Signatures, and Visibility


During analysis, we identified attributes and operations without necessarily specifying their types or
their parameters. During object design, we refine the analysis and system design models by completing
type and visibility information. The type of an attribute specifies the range of values the attribute can
take and the operations that can be applied to the attribute. For example, consider the attribute
maxNumPlayers of the Tournament class in ARENA (Figure 9-3). maxNumPlayers represent the
maximum number of Players who can be accepted in a given Tournament. Its type is int, denoting that it
is an integer number. The type of the maxNumPlayers attribute also defines the operations that can be
applied to this attribute: we can compare, add, subtract, or multiply other integers to maxNumPlayers.

Operation parameters and return values are typed in the same way as attributes are. The type
constrains the range of values the parameter or the return value can take. Given an operation, the tuple
made out of the types of its parameters and the type of the return value is called the signature of the
operation. For example, the acceptPlayer() operation of Tournament takes one parameter of type Player
and does not have a return value. The signature for acceptPlayer() is then acceptPlayer(Player):void.
Similarly, the getMaxNumPlayers() operation of Tournament takes no parameters and returns an int.
The signature of getMaxNumPlayers() is then getMaxNumPlayers(void):int.

The class implementor, the class user, and the class extender all access the operations and attributes of
the class under consideration. However, these developers have different needs and are usually not
allowed to access all operations of the class. For example, a class implementor accesses the internal data
structures of the class that the class user cannot see. The class extender accesses only selected internal
structures of superclasses. The visibility of an attribute or an operation is a mechanism for specifying
whether the attribute or operation can be used by other classes or not. UML defines four levels of
visibility:

• A private attribute can be accessed only by the class in which it is defined. Similarly, a private
operation can be invoked only by the class in which it is defined. Private attributes and operations
cannot be accessed by subclasses or calling classes. Private operations and attributes are intended for
the class implementor only.

A protected attribute or operation can be accessed by the class in which it is defined and by any
descendant of that class. Protected operations and attributes cannot be accessed by any other class.
Protected operations and attributes are intended for the class extender.

• A public attribute or operation can be accessed by any class. The set of public operations and
attributes constitute the public interface of the class and is intended for the class user.

• An attribute or an operation with visibility package can be accessed by any class in the nearest
enclosing package. This visibility enables a set of related classes (for example, forming a subsystem) to
share a set of attributes or operations without having to make them public to the entire system.

Visibility is denoted in UML by prefixing the name of the attribute or the operation with a character
symbol: – for private, # for protected, + for public, or ~ for package. For example, in Figure 9-3, we
specify that the maxNumPlayers attribute of Tournament is private, whereas all the class operations are
public.
Contracts: Invariants, Preconditions, and Postconditions
Contracts are constraints on a class that enable class users, implementors, and extenders to share the
same assumptions about the class [Meyer, 1997]. A contract specifies constraints that the class user
must meet before using the class as well as constraints that are ensured by the class implementor and
the class extender when used. Contracts include three types of constraints:

• An invariant is a predicate that is always true for all instances of a class. Invariants are constraints
associated with classes or interfaces. Invariants are used to specify consistency constraints among class
attributes.

• A precondition is a predicate that must be true before an operation is invoked. Preconditions are
associated with a specific operation. Preconditions are used to specify constraints that a class user must
meet before calling the operation.

• A postcondition is a predicate that must be true after an operation is invoked. Postconditions are
associated with a specific operation. Postconditions are used to specify constraints that the class
implementor and the class extender must ensure after the invocation of the operation.
Object Constraint Language
A constraint can be expressed in natural language or in a formal language such as Object Constraint
Language (OCL) [OMG, 2006]. OCL is a language that allows constraints to be formally specified on single
model elements (e.g., attributes, operations, classes) or groups of model elements (e.g., associations
and participating classes). In the next two sections, we introduce the basic syntax of OCL. For a complete
tutorial on OCL, we refer to [Warmer & Kleppe, 2003].

A constraint is expressed as a boolean expression returning the value True or False. A constraint can be
depicted as a note attached to the constrained UML element by a dependency relationship. Figure 9-4
depicts a class diagram of Tournament example of the previous section using UML and OCL.

OCL Collections: Sets, Bags, and Sequences


In general, constraints involve an arbitrary number of classes and attributes. Consider the class model of
Figure 9-6 representing the associations among the League, Tournament, and Player classes. Let’s
assume we want to refine the model with the following constraints:

1. A Tournament’s planned duration must be under one week.

2. Players can be accepted in a Tournament only if they are already registered with the corresponding
League.

3. The number of active Players in a League are those that have taken part in at least one Tournament of
the League.
An Overview of Mapping:
A transformation aims at improving one aspect of the model (e.g., its modularity) while preserving all of
its other properties (e.g., its functionality). Hence, a transformation is usually localized, affects a small
number of classes, attributes, and operations, and is executed in a series of small steps. These
transformations occur during numerous object design and implementation activities. We focus in detail
on the following activities:

• Optimization: This activity addresses the performance requirements of the system model. This
includes reducing the multiplicities of associations to speed up queries, adding redundant associations
for efficiency, and adding derived attributes to improve the access time to objects.

• Realizing associations: During this activity, we map associations to source code constructs, such as
references and collections of references.

• Mapping contracts to exceptions: During this activity, we describe the behavior of operations when
contracts are broken. This includes raising exceptions when violations are detected and handling
exceptions in higher level layers of the system.
• Mapping class models to a storage schema (Section 10.4.4). During system design, we selected a
persistent storage strategy, such as a database management system, a set of flat files, or a combination
of both. During this activity, we map the class model to a storage schema, such as a relational database
schema.

You might also like