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

OOSE Unit-4

Uploaded by

Jagadeesh
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
29 views

OOSE Unit-4

Uploaded by

Jagadeesh
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 79

GITAM School of Technology

19EAI334: OOSE Based Application


Development

1
Unit 4 Syllabus
Object Design: Design Patterns, Reusing Pattern Solutions
• Reuse Concepts. Reuse Activities: Selecting Design Patterns and Components,
Encapsulating Data Stores with the Bridge Pattern, Encapsulating Legacy Components
with the Adapter Pattern, Encapsulating Context with the Strategy Pattern ,Encapsulating
Platforms with the Abstract Factory Pattern, Encapsulating Control Flow with the
Command Pattern, Encapsulating Hierarchies with the Composite Design Pattern ,
Heuristics for Selecting Design Patterns , Identifying and Adjusting Application
Frameworks
Object Design: Specifying Interfaces
• Interface Specification Concepts: 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 Quantifiers: for All and exists

2
 During analysis, we describe the purpose of the system. This results in the
identification of application objects.

 During system design, we describe the system in terms of its architecture,


such as its subsystem decomposition, global control flow, and persistency
management.
 During system design, we also define the hardware/software platform
on which we build the system. This allows the selection of off-the-shelf
components that provide a higher level of abstraction than the
hardware.

 During object design, we close the gap between the application objects
and the off-the-shelf components by identifying additional solution objects
and refining existing objects.
3
Object Design: Reusing Pattern Solutions
Object design includes following activities:

Activities of object design (UML activity diagram).


• Reuse, during which we identify off-the-
shelf components and design patterns to
make use of existing solutions
• Service specification, during which we
precisely describe each class interface
• Object model restructuring, during which
we transform the object design model to
improve its understandability and
extensibility
• Object model optimization, during which
we transform the object design model to
address performance criteria such as
response time or memory utilization.
4
Reuse Concepts: Solution Objects,
Inheritance, and Design Patterns
Object design concepts related to reuse are:
• Application Objects and Solution Objects
• Specification Inheritance and Implementation Inheritance
• Delegation
• The Liskov Substitution Principle
• Delegation and Inheritance in Design Patterns

5
Application Objects and Solution
Objects
• 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.
6
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.
• 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. 7
• 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. 8
• 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 Figure 8-3 An example of implementation inheritance. The left column
implementing Set, because that would save us depicts a questionable implementation of MySet using implementation
inheritance. The right column depicts an improved implementation using
time, there is no taxonomy in which the Set delegation (UML class diagram and Java). 9
concept is related to the Hashtable concept.
• 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

Fig. 8-4
10
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:
1) 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.
2) 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.
11
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:

12
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. 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. 13
• 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) is a
template solution for such
problems.

Figure 8-5 An example of design pattern, Adapter (adapted from [Gamma


et al., 1994]).
14
• 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.
15
• 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 ClientInterface.
• 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

16
Reuse Activities: Selecting
Design Patterns and
Components
• On the one hand, during system design, we construct solid walls between subsystems
to manage complexity by breaking the system into smaller pieces and to prevent
changes in one subsystem from affecting other subsystems.
• On the other hand, during object design, we want the software to be modifiable and
extensible to minimize the cost of future changes.
• These are conflicting goals: we want to define a stable architecture to deal with
complexity, but we also want to allow flexibility to deal with change later in the
development process. This conflict can be solved by anticipating change and
designing for it, as sources of later changes tend to be the same for many systems:
• New vendor or new technology. Commercial components used to build the
system are often replaced by equivalent ones from a different vendor. This change
is common and generally difficult to cope with. The software marketplace is
dynamic, and vendors might go out of business before your project is
completed. 17
• New implementation. When subsystems are integrated and tested together, the overall
system response time is, more often than not, above performance requirements. System
performance is difficult to predict and should not be optimized before integration.
Developers should focus on the subsystem services first. This triggers the need for more
efficient data structures and algorithms, often under time constraints.

• New views. Testing the software with real users uncovers many usability problems. These
often translate into the need to create additional views on the same data.

• New complexity of the application domain. The deployment of a system triggers ideas
of new generalizations: a bank information system for one branch may lead to the
idea of a multi-branch information system. The application domain itself might also
increase in complexity: previously, flight numbers were associated with one plane, and
one plane only, but with air carrier alliances, one plane can now have a different flight
number from each carrier.

• Errors. Many requirements errors are discovered when real users start using the
system. 18
Decouples interface of class from implementation.
Developer not constrained with existing
components.
Decouples interface of class from implementation.
Developer is constrained with existing
components.
Decouples algorithm from implementation.
Implements code at runtime.

Creates families of related objects. Prevents use of


objects from incompatible families.

An object encapsulates all information needed to


perform an action. The information includes
method name, object that owns the method, etc.

Common superclass for aggregate and leaf nodes.


New types of leaves can be added without
modifying existing code. 19
Encapsulating Data Stores with the
Bridge Pattern
• Consider the problem of incrementally developing, testing, and integrating
subsystems realized by different developers.
• Subsystems may be completed at different times, delaying the integration of all
subsystems until the last one is completed. To avoid this delay, projects often use
a stub implementation in place of a specific subsystem so that the integration
tests can start even before the subsystems are completed.
• In other situations, several implementations of the same subsystem are realized,
such as a reference implementation that realizes the specified functionality with the
most basic algorithms, or an optimized implementation that delivers better
performance at the cost of additional complexity. In short, a solution is needed for
dynamically substituting multiple realizations of the same interface for
different uses.
20
• This problem can be addressed with the Bridge design pattern. For example, consider the storage of
Leagues in ARENA. In the early stages of the project, we are interested in a rudimentary storage
subsystem based on object serialization for the purpose of debugging and testing the core use cases
of the TournamentManagement subsystem. The entity objects will be subject to many changes, and
we do not know yet what performance bottlenecks will be encountered during storage.
Consequently, an efficient storage subsystem should not be the focus of the first prototype. As
discussed during the system design of ARENA, however, we anticipate that both a file-based
implementation and a relational database implementation of the storage subsystem should be
provided, in the first and second iteration of the system, respectively.
• In addition, a set of stubs should be provided to allow early integration testing even before the
file-based implementation is ready. To solve this problem, we apply the Bridge pattern shown in
Figure 8-7.
• The LeagueStore is the interface class to the pattern, and provides all high-level functionality
associated with storage. The LeagueStoreImplementor is an abstract interface that provides the
common interface for the three implementations, namely the StubStoreImplementor for the stubs,
the XMLStoreImplementor for the file-based implementation, and the JDBCStoreImplementor for
the relational database implementation. 21
• Note that even if most LeagueStoreImplementors provide similar services, using a
Bridge abstraction reduces performance. The design goals we defined at the
beginning of system design help us decide about performance and modifiability
trade-offs.
22
Inheritance and delegation in the
Bridge pattern
• The Bridge pattern interface is realized by the Abstraction class, and its behavior by the selected
ConcreteImplementor class. The design pattern can be extended by providing new
RefinedAbstraction or ConcreteImplementor classes. This pattern is a classic example of
combining specification inheritance and delegation to achieve both reuse and flexibility.
• On the one hand, specification inheritance is used between the abstract Implementor interface and
the classes ConcreteImplementors. As a result, each ConcreteImplementor can be substituted
transparently at runtime, from the Abstraction class and RefinedAbstraction classes. This also
ensures that, when adding a new ConcreteImplementor, developers will strive to provide the same
behavior as all other ConcreteImplementors.
• On the other hand, Abstraction and Implementor are decoupled using delegation. This enables the
distribution of different behavior in each of the side of the bridge. For example, the LeagueStore
class in Figure 8-7 provides the high-level behavior for storing Leagues, whereas the concrete
LeagueStoreImplementor provides specific lower-level functionality that differs in its realization
from one storage approach to the other. Since LeagueStore and LeagueStoreImplementor provide
different behaviors, they cannot be treated as subtypes according to the Liskov Substitution
Principle. 23
Encapsulating Legacy Components with the Adapter Pattern
• As the complexity of systems increases and the time to market shortens, the cost
of software development significantly exceeds the cost of hardware. Hence,
developers have a strong incentive to reuse code from previous projects or to use off-
the-shelf components.
• Interactive systems, for example, are now rarely built from scratch; they are
developed with user interface toolkits that provide a wide range of dialogs, windows,
buttons, or other standard interface objects.
• Interface engineering projects focus on reimplementing only part of an existing
system. For example, corporate information systems, costly to design and build, must
be updated to new client hardware. Often, only the client side of the system is
upgraded with new technology; the back end of the system left untouched.
• Whether dealing with off-the-shelf component or legacy code, developers have to deal
with code they cannot modify and which usually was not designed for their system.

24
• We deal with existing components by encapsulating them. This approach has the advantage of
decoupling the system from the encapsulated component, thus minimizing the impact of existing
software on the new design. This can be done using an Adapter pattern.
• The Adapter design pattern converts the interface of a component into an interface that the client
expects. This interface is called the ClientInterface in Figure 8-5. An Adapter class provides the
glue between ClientInterface and LegacyClass. For example, assume the client is the static sort()
method of the Java Array class (Figures 8-8 and 8-9). This method expects two arguments a, an
Array of objects, and c, a Comparator object, which provides a compare() method to define the
relative order between elements. Assume we are interested in sorting strings of the class
MyString, which defines the greaterThan() and an equals() methods. To sort an Array of
MyStrings, we need to define a new comparator, MyStringComparator, which provides a
compare() method using greaterThan() and equals(). MyStringComparator is an Adapter class.

Figure 8-8 Applying the Adapter design pattern


for sorting Strings in an Array (UML class
diagram). See also source code in Figure 8-9.
25
26
Inheritance and delegation in the Adapter pattern
• The Adapter pattern uses specification inheritance between the ClientInterface and the
Adapter. The Adapter in turn delegates to the LegacyClass implementor class to realize the
operations declared in ClientInterface. On the one hand, this enables all client code that
already uses the ClientInterface to work with instances of Adapter transparently and
without modification of the client. On the other hand, the same Adapter can be used for
subtypes of the LegacyClass.
• Note that the Bridge and the Adapter patterns are similar in purpose and structure. Both
decouple an interface from an implementation, and both use a specification inheritance
relationship and a delegation relationship. They differ in the context in which they are
used and in the order in which delegation and inheritance occur.
• The Adapter pattern uses inheritance first and then delegation, whereas the Bridge pattern
uses delegation first and then inheritance.
• The Adapter pattern is applied when the interface (i.e., ClientInterface) and the
implementation (i.e., LegacyClass) already exist and cannot be modified. When
developing new code, the Bridge pattern is a better choice as it provides more
extensibility. 27
Encapsulating Context with the Strategy Pattern
• Consider a mobile application running on a wearable computer that uses different
networks protocols depending on the location of the user: assume, for example, a car
mechanic using the wearable computer to access repair manuals and maintenance
records for the vehicle under repair. The wearable computer should operate in the shop
with access to a local wireless network as well as on the roadside using a third-
generation mobile phone network, such as UMTS. When updating or configuring the
mobile application, a system administrator should be able to use the wearable
computer with access to a wired network such as Ethernet. This means that the mobile
application needs to deal with different types of networks as it switches between
networks dynamically, based on factors such as location and network costs.
• Assume that during the system design of this application, we identify the dynamic
switching between wired and wireless networks as a critical design goal. Furthermore
we want to be able to deal with future network protocols without having to recompile
the application.

28
• To achieve both of these goals, we apply the
Strategy design pattern. The system model and
implementation, respectively, are shown in Figures
8-10 and 8-11. The Strategy class is realized by
NetworkInterface, which provides the common
interface to all networks; the Context class is
realized by a NetworkConnection object, which
represents a point-to-point connection between the
wearable and a remote host.
• The Client is the mobile application. The Policy is
the LocationManager, which monitors the current
location of the wearable and the availability of
networks, and configures the NetworkConnection Figure 8-10 Applying the Strategy pattern for encapsulating
objects with the appropriate NetworkInterfaces. multiple implementations of a NetworkInterface (UML
When the LocationManager object invokes the class diagram). The LocationManager implementing a
setNetworkInterface() method, the specific policy configures NetworkConnection with a
NetworkConnection object shuts down the current concrete NetworkInterface (i.e., the mechanism) based on
NetworkInterface and initializes the new the current location. The Application uses the
NetworkInterface transparently from the rest of NetworkConnection independently of concrete
NetworkInterfaces. See corresponding Java code in Figure
the application.
8-11. 29
Inheritance and delegation in the Strategy pattern
• The class diagrams for the Bridge and the Strategy patterns (see Figures 8-7 and 8-10) are almost identical. The key
difference is in the creator of the concrete implementation classes: In the Bridge pattern, the class Abstraction creates
and initializes the ConcreteImplementations.
• In the Strategy pattern, however, the Context is not aware of the ConcreteStrategies. Instead, a client creates the
ConcreteStrategy objects and configures the Context. Moreover, ConcreteImplementations in the Bridge pattern are
usually created at initialization time, while ConcreteStrategies in the Strategy pattern are usually created and substituted
several times during run time.

30
Encapsulating Platforms with the Abstract Factory
Pattern
• Consider an application for an intelligent house: the application receives events
from sensors distributed throughout the house (e.g., light bulb on, light bulb off,
window open, window closed, inside and outside temperature, weather
forecasts), identifies predefined patterns, and issues commands for actuators (e.g.,
turn air-conditioning on, store statistics on energy consumption, close garage door,
trigger theft alarm).
• Although several manufacturers provide the hardware to build such applications
(e.g., EIB, Zumtobel’s Luxmate), interoperability in this domain is currently
poor, preventing the mix and match of devices from different manufacturers, and
thus, making it difficult to develop a single software solution for all
manufacturers.
• We use the Abstract Factory design pattern to solve this problem. In our intelligent
house, each manufacturer provides temperature sensors, electric blinds that report
if they are forced in, and intelligent light bulbs that report if they have burned out.
31
• As shown in Figure 8-12, these generic objects
are called AbstractProducts (e.g., LightBulb,
Blind), and their concrete realizations are called
ConcreteProducts (e.g., EIBBulb,
LuxmateBulb, EIBBlind, LuxmateBlind).
• One factory for each manufacturer (e.g.,
LuxmateFactory, EIBFactory) provides methods
for creating the ConcreteProducts (e.g.,
createLightBulb(), createBlind()).
• The Client classes (e.g., a TheftApplication)
access only the interfaces provided by the
AbstractFactory and the AbstractProducts,
thereby shielding the Client classes completely
from the manufacturer of the underlying Figure 8-12 Applying the Abstract Factory design pattern to
products. different intelligent house platforms (UML class diagram,
dependencies represent «call» relationships).

32
Inheritance and delegation in the Abstract Factory pattern
• The Abstract Factory pattern uses specification inheritance to decouple the
interface of a product from its realization. However, since products of the same
platform usually depend on each other and access the concrete product classes,
products of different platforms cannot be substituted transparently.
• For example, EIBBulbs are incompatible with LuxmateBulbs and should not be
mixed within the same intelligent house system. To ensure that a consistent set of
products is created, the Client can only create products by using a
ConcreteFactory, which delegates the creation operations to the respective
products.
• By using specification inheritance to decouple ConcreteFactories from their
interface, product families from different manufacturers can be substituted
transparently from the client

33
Encapsulating Control Flow with the Command
Pattern
• In interactive systems and in transaction systems, it is often desirable to execute, undo, or
store user requests without knowing the content of the request. For example, consider the
case of matches in the ARENA tournament management system. We want to record individual
moves in matches so that these moves can be replayed by a spectator at a later date. However,
we also want ARENA to support a broad spectrum of games, so we do not want the classes
responsible for recording and replaying moves to depend on any specific game.
• We can apply the Command design pattern to this effect. The key to decoupling game moves
from their handling is to represent game moves as command objects that inherit from an
abstract class called Move in Figure 8-13. The Move class declares operations for executing,
undoing, and storing commands, whereas ConcreteCommands classes (i.e.,
TicTacToeMove and ChessMove in ARENA) implement specific commands. The classes
responsible for recording and replaying games only access the GameMove abstract class
interface, thus making the system extensible to new Games.
34
Figure 8-13 Applying the Command design pattern to
Matches in ARENA (UML class diagram).

Inheritance and delegation in the Command pattern


• The Command design pattern uses specification inheritance between the Command class
and ConcreteCommands, enabling new commands to be added independently from the
Invoker. Delegation is used between ConcreteCommands and Receivers, and between
Invoker and Command, enabling ConcreteCommands to be dynamically created,
executed, and stored.
• The Command pattern is often used in a Model/View/Controller software architecture,
where Receivers are model objects, Invoker and Commands are controller objects, and
Clients creating Commands are view objects.
35
Encapsulating Hierarchies with the Composite
Design Pattern
• User interface toolkits, such as Swing and Cocoa, provide the application developer with a
range of classes as building blocks. Each class implements a specialized behavior, such as
inputting text, selecting and deselecting a check box, pushing a button, or pulling down a
menu. The user interface design can aggregate these components into Windows to build
application-specific interfaces. For example, a preferences dialog may include a number of on-
off check boxes for enabling different features in the application.
• As windows become more complex and include many different user interface objects, their
layout (i.e., moving and resizing each component so that the window forms a coherent whole)
becomes increasingly unmanageable. Consequently, modern toolkits enable the developer to
organize the user interface objects into hierarchies of aggregate nodes, called “panels,” that
can be manipulated the same way as the concrete user interface objects. For example, our
preferences dialog can include a top panel for the title of the dialog and instructions for the
user, a center panel containing the checkboxes and their labels, and a bottom panel for the ‘ok’
and ‘cancel’ button. Each panel is responsible for the layout of its subpanels, called “children,”
and the overall dialog only has to deal with the three panels (Figures 8-14 and 8-15).
36
Figure 8-15 UML object diagram for the user interface
objects of Figure 8-14.

37
• Swing addresses this problem with the Composite design pattern as depicted in Figure 8-16. An abstract
class called Component is the roof of all user interface objects, including Checkboxes, Buttons, and
Labels. Composite, also a subclass of Component, is a special user interface object representing aggregates
including the Panels we mentioned above. Note that Windows and Applets (the root of the instance
hierarchy) are also Composite classes that have additional behavior for dealing with the window manager
and the browser, respectively.

Figure 8-16 Applying the Composite design pattern to user interface widgets (UML class diagram). The Swing Component
hierarchy is a Composite in which leaf widgets (e.g., Checkbox, Button, Label) specialize the Component interface, and
aggregates (e.g., Panel, Window) specialize the Composite abstract class. Moving or resizing a Composite impacts all of its
children. 38
Heuristics for Selecting Design
Patterns
Natural language heuristics for selecting
• Identifying the correct design pattern for a design patterns
given problem is difficult unless you already
have some experience in using design patterns.
Pattern catalogs are large and varied, and one
cannot expect developers to read them
completely. As design patterns address a
specific design goal or a specific nonfunctional
requirement, another technique is to use key
phrases in the Requirements Analysis
Document (RAD) and the System Design
Document (SDD) to select candidate patterns.
This is similar to the Abbott’s natural language
technique.
39
Identifying and Adjusting
Application Frameworks
Application frameworks
• An application framework is a reusable partial application that can be specialized
to produce custom applications. In contrast to class libraries, frameworks are
targeted to particular technologies, such as data processing or cellular
communications, or to application domains, such as user interfaces or real-time
avionics.
• The key benefits of application frameworks are reusability and extensibility.
Framework reusability leverages the application domain knowledge and the prior
effort of experienced developers to avoid recreation and revalidation of recurring
solutions. An application framework enhances extensibility by providing hook
methods, which are overwritten by the application to extend the application
framework. Hook methods systematically decouple the interfaces and behaviors of
an application domain from the variations required by an application in a particular
context. Framework extensibility is essential to ensure timely customization of new
application services and features. 40
Frameworks can be classified by their position in the software development process.
• Infrastructure frameworks aim to simplify the software development process. Examples include
frameworks for operating systems, debuggers, communication tasks, user interface design, and Java
Swing. System infrastructure frameworks are used internally within a software project and are usually not
delivered to a client.
• Middleware frameworks are used to integrate existing distributed applications and components.
Common examples include Microsoft’s MFC and DCOM, Java RMI, WebObjects, WebSphere [IBM],
WebLogic Enterprise Application [BEA], implementations of CORBA, and transactional databases.
• Enterprise application frameworks are application specific and focus on domains such as
telecommunications, avionics, environmental modeling, manufacturing, financial engineering, and
enterprise business activities.

Frameworks can also be classified by the techniques used to extend them.


• Whitebox frameworks rely on inheritance and dynamic binding for extensibility. Existing functionality
is extended by subclassing framework base classes and overriding predefined hook methods using
patterns.
• Blackbox frameworks support extensibility by defining interfaces for components that can be plugged
into the framework. Existing functionality is reused by defining components that conform to a particular
interface and integrating these components with the framework using delegation.
41
• Whitebox frameworks require intimate knowledge of the framework’s internal
structure. Whitebox frameworks produce systems that are tightly coupled to the
specific details of the framework’s inheritance hierarchies, and thus changes in the
framework can require the recompilation of the application.

• Blackbox frameworks are easier to use than whitebox frameworks because they
rely on delegation instead of inheritance. However, blackbox frameworks are
more difficult to develop because they require the definition of interfaces and
hooks that anticipate a wide range of potential use cases. Moreover, it is easier to
extend and reconfigure blackbox frameworks dynamically, as they emphasize
dynamic object relationships rather than static class relationships.

42
Frameworks, class libraries, and design patterns
Frameworks are closely related to design patterns, class libraries, and components.
1. Design patterns versus frameworks. The main difference between frameworks and patterns is that
frameworks focus on reuse of concrete designs, algorithms, and implementations in a particular
programming language. In contrast, patterns focus on reuse of abstract designs and small
collections of cooperating classes. Frameworks focus on a particular application domain, whereas
design patterns can be viewed more as building blocks of frameworks.
2. Class libraries versus frameworks. Classes in a framework cooperate to provide a reusable
architectural skeleton for a family of related applications. In contrast, class libraries are less domain
specific and provide a smaller scope of reuse. For instance, class library components, such as classes
for strings, complex numbers, arrays, and bitsets, can be used across many application domains.
Class libraries are typically passive; that is, they do not implement or constrain the control flow.
Frameworks, however, are active; that is, they control the flow of control within an application. In practice,
developers often use frameworks and class libraries in the same system. For instance, frameworks use class
libraries, such as foundation classes, internally to simplify the development of the framework. Similarly,
application-specific code invoked by framework event handlers uses class libraries to perform basic tasks,
such as string processing, file management, and numerical analysis.
43
3. Components versus frameworks. Components are self-contained instances of classes that are
plugged together to form complete applications. In terms of reuse, a component is a blackbox that
defines a cohesive set of operations that can be used solely with knowledge of the syntax and
semantics of its interface. Compared with frameworks, components are less tightly coupled and can
even be reused on the binary code level. That is, applications can reuse components without having
to subclass from existing base classes.

The advantage is that applications do not always have to be recompiled when components change.
The relationship between frameworks and components is not predetermined. On the one hand,
frameworks can be used to develop components, where the component interface provides a facade
pattern for the internal class structure of the framework. On the other hand, components can be
plugged into blackbox frameworks. In general, frameworks are used to simplify the development of
infrastructure and middleware software, whereas components are used to simplify the development
of end-user application software.

44
A framework example
• WebObjects is a set of frameworks for
developing interactive Web applications
by accessing existing data from relational
databases.
• WebObjects consists of two infrastructure
frameworks.
• The WebObjects framework handles the
interaction between Web browsers and
Web servers.
• The Enterprise Object Framework
(EOF) handles the interaction between
Web servers and relational databases.
The EOF supports database adapters that
allow applications to connect to database
management systems from particular
vendors.
• For example, the EOF provides database Figure 8-17 An example of dynamic site with WebObjects (UML
component diagram).
adapters for Informix, Oracle, and Sybase
servers and ODBC-compliant adapters. 45
• Figure 8-17 shows an example of a dynamic publishing site built with WebObjects. The WebBrowser
originates an HTTP request containing a URL, which is sent to the WebServer. If the WebServer detects
that the request is to a static HTML page, it passes it on the StaticHTML object, which selects and sends
the page back to the WebBrowser as a response. The WebBrowser then renders it for the user. If the
WebServer detects that the request requires a dynamic HTML page, it passes the request to a WebObjects
WOAdaptor. The WOAdaptor packages the incoming HTML request and forwards it to the
WebObjectsApplication object. Based on Templates defined by the developer and relevant data retrieved
from the RelationalDatabase, the WebObjectsApplication then generates an HTML response page, which
is passed back through the WOAdaptor to the WebServer. The WebServer then sends the page to the
WebBrowser, which renders it for the user.
• A key abstraction provided by the WebObjects framework is an extension of the HTTP protocol to manage
state. HTTP is a stateless request-response protocol; that is, a response is formulated for each request, but
no state is maintained between successive requests. In many Web-based applications, however, state must
be kept between requests. For example in ARENA, Players should not identify themselves for each move
they play. Moreover, Players must be able to continue playing in the same Match even if their
WebBrowser is restarted. Several techniques have been proposed to keep track of state information in Web
applications, including dynamically generated URLs, cookies, and hidden HTML fields. WebObjects
provides the classes shown in Figure 8-18 to achieve the same purpose.
46
Figure 8-18 WebObject’s State Management Classes. The HTTP protocol is inherently stateless. The State Management
Classes enable information between individual requests to be maintained.

47
• The WOApplication class represents the application running on the WebServer waiting for
requests from the associated WebBrowser. A cycle of the request-response loop begins whenever
the WOAdaptor receives an incoming HTTP request. The WOAdaptor packages this request in a
WORequest object and forwards it to the application object of class WOApplication. Requests
are always triggered by a URL submitted by the WebBrowser. The class WOSession
encapsulates the state of an individual session, allowing it to track different users, even within a
single application. A WOSession consists of one or more WOComponents, which represent a
reusable Web page or portion of a Web page for display within an individual session.
WOComponents may contain DynamicElements. When an application accesses the database,
one or more of the DynamicElements of a component are filled with information retrieved from
the database. The WOSessionStore provides persistency for WOSession objects: it stores
sessions in the server and restores them by the application upon request.
• The essence of building a WebObjects application is to refine the classes WOApplication,
WOSession, and WOComponent and to intercept the flow of requests sent and received between
them. Inherited methods from these classes are overridden when the developer needs to extend
the default behavior. The earliest control point for refining objects of type WOApplication is
when they are constructed. The last point of control is when the application object terminates.
By adding code to the application object constructor or overriding the WOApplication
terminate() method, the developer can customize the behavior of the WebObjects application 48
as
desired.
Object Design: 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.
The interface specification activities of object design include:
• identifying missing attributes and operations
• specifying type signatures and visibility
• specifying invariants
• specifying preconditions and postconditions.

49
An Overview of Interface
Specification
• 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.

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

51
Interface Specification Concepts
• 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

52
Class Implementor, Class Extender,
and Class User
So far, we have treated all developers as equal. Now that we are examining 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 (see also Figure 9-1):
• 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
specifies both the current behavior of the class and any constraints on the services provided by the
specialized class. 53
Figure 9-1 The Class Implementor, the Class Extender, and the Class User role (UML use case diagram).

• 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.
54
Figure 9-2 ARENA Game abstract class with user classes and extender classes.

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

56
• 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:
57
• 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.
58
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.

Figure 9-3 Declaration for the Tournament class (UML class model and Java excerpts).

59
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. 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.
60
• For example, consider the Java interface for the Tournament from Figure 9-3. This class
provides an acceptPlayer() method to add a Player in the Tournament, a removePlayer() method
to withdraw a Player from the Tournament (e.g., because the player cancelled his application),
and a getMaxNumPlayers() method to get the maximum number of Players who can participate
in this Tournament.
• An example of an invariant for the Tournament class is that the maximum number of
Players in the Tournament should be positive. If a Tournament is created with a
maxNumPlayers that is zero, the acceptPlayer() method will always violate its contract and the
Tournament will never start. Using a boolean expression, in which t is a Tournament, we can
express this invariant as
t.getMaxNumPlayers() > 0
• An example of a precondition for the acceptPlayer() method is that the Player to be added has
not yet already been accepted in the Tournament and that the Tournament has not yet reached
its maximum number of Players. Using a boolean expression, in which t is a Tournament and p
is a Player, we express this invariant as
!t.isPlayerAccepted(p) and t.getNumPlayers() < t.getMaxNumPlayers() 61
• An example of a postcondition for the acceptPlayer() method is that the current
number of Players must be exactly one more than the number of Players before
the invocation of acceptPlayer(). We can express this postcondition as

t.getNumPlayers_afterAccept = t.getNumPlayers_beforeAccept + 1
where numPlayers_beforeAccept and numPlayers_afterAccept are the current
number of Players before and after acceptPlayer(), respectively.

• We use invariants, preconditions, and postconditions to specify special or


exceptional cases unambiguously. It is also possible to use constraints to
completely specify the behavior of an operation. Such a use of constraints, called
“constraint-based specification,” however, is difficult and can be more
complicated than implementing the operation itself.
62
Object Constraint Language
• A constraint can be expressed in natural language or in a formal language such as Object Constraint
Language (OCL). 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).
• 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.

Figure 9-4 Examples of invariants, preconditions, and postconditions in OCL attached as notes to the UML model
(UML class diagram). 63
• Attaching OCL expressions to diagrams can lead to clutter. For this reason, OCL
expressions can be alternatively expressed in a textual form. For example, the
invariant for the Tournament class requiring the attribute maxNumPlayers to be
positive is written as follows:

• The context keyword indicates the entity to which the expression applies. This is
followed by one of the keywords inv, pre, and post, which correspond to the UML
stereotypes «invariant», «precondition», and «postcondition», respectively. Then
follows the actual OCL expression. OCL’s syntax is similar to object-oriented
languages such as C++ or Java. However, OCL is not a procedural language and
thus cannot be used to denote control flow. Operations can be used in OCL
expressions only if they do not have any side effects.

64
• For invariants, the context for the expression is the class associated with the
invariant. The keyword self (e.g., self.numElements) denotes all instances of the
class. Attributes and operations are accessed using the dot notation (e.g.,
self.maxNumPlayers accesses maxNumPlayers in the current context). The self
keyword can be omitted if there is no ambiguity.
• For preconditions and postconditions, the context of the OCL expression is an
operation. The parameters passed to the operation can be used as variables in the
expression. For example, consider the following precondition on the
acceptPlayer() operation in Tournament:

65
• The variable p in the constraint !isPlayerAccepted(p) refers to the parameter p
passed to the acceptPlayer(p) operation. As this is a precondition, the constraint
must be True before the execution of the acceptPlayer(p) operation. Hence, the
constraint reads in English: “acceptPlayer(p) assumes that p has not yet been
accepted in the Tournament”. We can write several preconditions for the same
operation. If there are more than one precondition for a given operation, all
preconditions must be True before the operation can be invoked. For example, we
can also state that the Tournament must not yet have reached the maximum
number of Players before invoking acceptPlayer():

66
• Post conditions are written in the same way as preconditions, except for the
keyword post indicating that the constraint is evaluated after the operation returns.
For example, the following postcondition on acceptPlayer(p) states that the Player
p should be known to the Tournament after acceptPlayer() returns:

• For postconditions, we often need to refer to the value of an attribute before and
after the execution of the operation. For this purpose, the suffix @pre denotes the
value of self or an attribute before the execution of the operation. For example, if
we want to state that the number of Players in the Tournament increases by one
with the invocation of acceptPlayer(), we need to refer to the value of
getNumPlayers() before and after the invocation of acceptPlayer(). We can write
the following postcondition:

67
• @pre.getNumPlayers() denotes the value returned by getNumPlayers() before
invoking acceptPlayer(), and getNumPlayers() denotes the value returned by the
same operation after invoking acceptPlayer(). Similar to preconditions, if there is
more than one postcondition for a given operation, all postconditions must be
satisfied after the operation completes. We can therefore write the contract for the
removePlayer() operation with the same approach:

68
Figure 9-5 Method declarations for the Tournament class
annotated with preconditions, postconditions, and invariants
(Java, constraints using Javadoc style tags).

69
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. Figure 9-6 Associations among League,
Tournament, and Player classes in ARENA.
70
• To better understand these constraints, let’s examine them for a specific group of
instances (see Figure 9-7). The tttExpert:League includes four Players, alice, bob,
marc, and joe, and two Tournaments, a winter:Tournament and a
xmas:Tournament. alice and bob are competing in the winter:Tournament while
bob, marc, and joe are competing in the xmas:Tournament. The
chessNovice:League currently only includes one Player, zoe, and no Tournaments.
Now, let’s review the above constraints in terms of the instances of Figure 9-7:
1. The winter:Tournament lasts two days, the xmas:Tournament three days, both
under a week.
2. All Players of the winter:Tournament and the xmas:Tournament are associated
with tttExpert:League. The Player zoe, however, is not part of the
tttExpert:League and does not take part in either Tournament.
3. tttExpert:League has four active Players, whereas the chessNovice:League has
none, because zoe does not take part in any Tournament.

71
Figure 9-7 Example with two Leagues, two Tournaments, and five Players (UML object diagram).
72
• At first sight, these constraints vary quite a bit: for example, the first constraint
involves attributes of a single class (Tournament.start and Tournament.end); the
second one involves three classes (i.e., Player, Tournament, League) and their
associations; the third involves a set of Matches within a single Tournament. In all
cases, we start with the class of interest and navigate to one or more classes in the
model. In general, we distinguish three cases of navigation (Figure 9-8):
• Local attribute. The constraint involves an attribute that is local to the class of
interest (e.g., duration of a Tournament in constraint 1),
• Directly related class. The expression involves the navigation of a single
association to a directly related class (e.g., Players of a Tournament, League of a
Tournament).
• Indirectly related class. The constraint involves the navigation of a series of
associations to an indirectly related class (e.g., the Players of all Tournaments of a
League).

73
Figure 9-8 There are only three basic types of navigation. Any OCL constraint can be built using a combination of these
three types.

74
• All constraints can be built using a combination of these three basic cases of
navigation. Once we know how to deal with these three navigation cases, we can
build any constraint. We already know how to deal with the first type of constraint
with the dot notation, as we saw in the previous section. For example, we can
write constraint 1 as follows:

In the second constraint, however, the expression league.players can actually refer
to many objects, since the players association is a many-to-many association. To
deal with this situation, OCL provides additional data types called collections.
There are three types of collections:

75
• OCL sets are used when navigating a single association. For example, navigating the players
association of the winter:Tournament yields the set {alice, bob}. Navigating the players
association from the tttExpert:League yields the set {alice, bob, marc, joe}. Note, however, that
navigating an association of multiplicity 1 yields directly an object, not a set. For example,
navigating the league association from winter:Tournament yields tttExpert:League (as opposed
to {tttExpert:League}).

• OCL sequences are used when navigating a single ordered association. For example, the
association between League and Tournament is ordered. Hence, navigating the tournaments
association from tttExpert:League yields [winter:Tournament, xmas:Tournament] with the
index of winter:Tournament and xmas:Tournament being 1 and 2, respectively.

• OCL bags are multisets: they can contain the same object multiple times. Bags are used to
accumulate the objects when accessing indirectly related objects. For example, when
determining which Players are active in the tttExpert:League, we first navigate the
tournaments association of tttExpert, then the players association from winter:Tournament,
and finally the players association from xmas:Tournament, yielding the bag {alice, bob, bob,
marc, joe}. The bag resulting from navigating the same associations from chessNovice:League
results in the empty bag, as there are no Tournaments in the chessLeague. In cases where the
number of occurrences of each object in the bag is undesired, the bag can be converted to a set.
76
OCL provides many operations for accessing collections. The most often used are:
• size, which returns the number of elements in the collection
• includes(object), which returns True if object is in the collection
• select(expression), which returns a collection that contains only the elements of
the original collection for which expression is True
• union(collection), which returns a collection containing elements from both
the original collection and the collection specified as parameter
• intersection(collection), which returns a collection that contains only the
elements that are part of both the original collection and the collection
specified as parameter
• asSet(collection), which returns a set containing each element of the collection.

77
OCL Quantifiers: forAll and
exists
So far, we presented examples of constraints using common OCL collection
operations such as includes, union, or asSet. Two additional operations on
collections enable us to iterate over collections and test expressions on each
element:
• forAll(variable|expression) is True if expression is True for all elements in the
collection.
• exists(variable|expression) is True if there exists at least one element in the
collection for which expression is True.

78
• For example, to ensure that all Matches in a Tournament occur within the
Tournament’s time frame, we can repetitively test the start dates of all matches
against the Tournament using forAll(). Consequently, we write this constraint as
follows:

• The OCL exists() operation is similar to forAll(), except that the expressions
evaluated on each element are ORed, that is, only one element needs to satisfy the
expression for the exists() operation to return True. For example, to ensure that
each Tournament conducts at least one Match on the first day of the Tournament,
we can write:

79

You might also like