Task: Identify Design Elements |
|
|
This task explains how to identify Subsystems, Classes, interfaces, Events and Signals. |
|
Purpose
-
To analyze interactions
of analysis classes to identify design model elements
|
Relationships
Roles | Main:
| Additional:
| Assisting:
|
Inputs | Mandatory:
| Optional:
| External:
|
Outputs |
|
Main Description
The Task: Use Case Analysis results in analysis classes, which
represent conceptual things which can perform behavior. In design, analysis classes evolve into a number
of different kinds of design elements:
-
classes, to represent a set of rather fine-grained responsibilities;
-
subsystems, to represent a set of coarse-grained responsibilities, perhaps composed of a further set of subsystems,
but ultimately a set of classes;
-
active classes, which represent threads in the system;
-
interfaces, to represent abstract declarations of responsibilities provided by a class or subsystem.
In addition, in design we shall also identify:
-
events, which are specifications of interesting occurrences in time and space that usually (if they are noteworthy)
require some response from the system; and
-
signals, to represent asynchronous mechanisms used to communicate certain types of events within the system.
These finer distinctions enable us to examine different aspects of the design:
-
Events and the Signals that are used to communicate them, allow us to describe the asynchronous triggers of
behavior to which the system must respond.
-
Classes and Subsystems allow us to group related responsibilities into units which can be developed in relative
independence; classes fulfill an atomic set of related responsibilities, while subsystems are composite building
blocks which are in turn composed of classes or other subsystems. Subsystems are used to represent the work
products of a development team as a single, integral unit of functionality, and as such are used both as units of
control and configuration management as well as logical design elements.
-
Active classes are used to represent control threads in the system, allowing the modeling of concurrency. Active
classes are often used in composition with other classes that are usually, but not necessarily, passive: such a
composition can then be used - in the same way as a collaboration - to model complex behavior.
In real-time systems, capsules are used in place of active classes, offering stronger semantics to simplify the
design and increase the reliability of concurrent applications. Capsules share some aspects of both classes and
subsystems: they are in fact encapsulated collaborations of classes which together represent a thread of
control in the system. They differ from subsystems in the sense that a capsule is the responsibility of a
single designer, whereas a subsystem is the responsibility (typically) of a team of developers; a subsystem may
contain capsules, however.
-
Interfaces allow us to examine and capture the 'seams' of the system, defining in precise terms how the constituent
parts of the system will interoperate.
-
In real-time systems, we shall use Protocols to define precisely the messages that may be sent and received on a
port of a capsule.
By separating concerns and handling each issue represented by these concepts separately, we simplify the design process
and clarify our solution.
If traceability is to be maintained between system models, it should be documented during this task. For more
information on documenting the traceability between the Design Model and other system models, see Guideline: Design Model.
UML 1.x Representation
According UML 1.5, a subsystem is, effectively, a special kind of package which has only interfaces as public elements.
The interfaces provide a layer of encapsulation, allowing the internal design of the subsystem to remain hidden from
other model elements. The concept subsystem is used to distinguish it from "ordinary" packages, which are semantic-free
containers of model elements; the subsystem represents a particular usage of packages with class-like (behavioral)
properties.
In RUP, Capsules are represented using UML 1.5 notation. Much of this can be represented in UML 2.0 using the Concept: Structured Class.
Refer to Differences Between UML 1.x and UML 2.0 for more information.
|
Steps
Identify Events and Signals
Purpose
|
To identify the external and internal events and signals to which the system must respond.
|
Events are external and internal occurrences which cause some action within the system. Events and their
characteristics can help drive the identification of key design elements, such as active classes.
An initial list of external events can be derived from the Use-Case Model, from the actors' interactions with use
cases. Internal events may be derived from text in the use case flows, or may be identified as the design evolves.
Important characteristics of events are:
-
internal vs. external - Is the event external or internal?
-
priority - Does this event need to cause the suspension of other processing in order to be handled?
-
frequency - How often does the event occur?
-
frequency distribution - Does the event occur at regular intervals, or are there spikes?
-
response requirements - How the quickly the system must respond to the event (may need to distinguish between
average and worst case).
-
kind - Is this a Call Event, Time Event, Signal Event, or Change Event (see Concept: Events and Signals for definitions)?
Events' characteristics should be captured as needed to drive the identification of the design elements that handle
them. Capturing event characteristics tends to be most important in reactive (event-driven) systems, but it can be
useful in other systems, such as those with concurrency and/or asynchronous messaging.
Asynchronous communication events can be modeled as Signals to express the data that they carry, or to express
relationships between signals, such as generalization. In some systems, in particular reactive systems, it is
important to relate signals received from external devices to specific mechanisms, such as interrupts or specific
polling messages.
|
Identify Classes, Active Classes and Subsystems
Purpose
|
To refine the analysis classes into appropriate design model elements
|
Identify Classes. When the analysis class is simple and already represent a single logical abstraction, it can
be directly mapped, 1:1, to a design class. Typically, entity classes survive relatively intact into Design.
Since entity classes are typically also persistent, determine whether the design class should be persistent and note it
accordingly in the class description.
When identifying classes, they should be grouped into Artifact: Design Packages, for organizational and configuration
management purposes. See Guideline: Design Package for more information on how to make packaging decisions.
Identify Active Classes. Consider the concurrency requirements of the system in the context of the analysis
objects identified: is there a need for the system to respond to externally generated events, and if so, which analysis
classes are 'active' when the events occur? External events in the Use-Case Model are represented by stimuli coming
from actors, interacting with a use case. Look at the corresponding Use-Case Realizations to see which objects interact
when an event occurs. Start by grouping the objects together into autonomous sets of collaborating objects - these
groupings represent an initial cut at a group that may form a composite active class.
If the events have important attributes that need to be captured, consider modeling them as classes, stereotyped
<<signal>>. In real-time systems, these identified sets of objects should be grouped into capsules, which
have strong encapsulation semantics.
The instances of active classes represent independent 'logical' threads of execution. These 'logical' threads of
execution are not to be confused with or mapped literally to threads of execution in the operating system (though at
some point we will map them to operating system threads of execution). Instead, they represent independent conceptual
threads of execution in the solution space. Our goal in identifying them at this point in design is to be able to
partition the solution into independent units based on natural 'concurrency seams' in the system. Dividing the work in
this way makes the problems of dealing with concurrency conceptually simpler, since independent threads of execution
can be dealt with separately except to the extent that they share underlying passive classes.
In general, an active class should be considered whenever there exist concurrency and concurrency conflicts in the
problem domain. An active class should be used to represent some external concurrent object or concurrent activity
within the computer. This gives us the ability to monitor and control concurrent activities.
Another natural choice is to use active classes as internal representatives of external physical devices that
are connected to a computer since those physical entities are inherently concurrent. These "device driver" classes
serve not only to monitor and control the corresponding physical devices but they also isolate the rest of the system
from the specifics of the devices. This means that the rest of the system may not be affected even if the technology
behind the devices evolves.
Another common place for using active classes is to represent logical concurrent activities. A logical activity
represents a conceptual concurrent "object", such as, for example, a financial transaction or a telephone call. Despite
the fact that these are not directly manifested as physical entities (although they take place in the physical world),
there are often reasons to treat them as such. For instance, we may need to temporarily hold back a particular
financial transaction to avoid a concurrency conflict or we may need to abort it due to failures within the system.
Since these conceptual objects need to be manipulated as a unit, it is convenient to represent them as objects with
interfaces of their own that provide the appropriate functional capabilities.
A particular example of this type of conceptual object is an active object controller. Its purpose is to
continuously manage one or more other active objects. This normally involves bringing each object into the desired
operational state, maintaining it in that state in the face of various disruptions such as partial failures, and
synchronizing its operation with the operation of other objects. These active object controllers often evolve from
Control objects identified during Task: Use-Case Analysis.
Because of their capacity to simply and elegantly resolve concurrency conflicts, active classes are also useful as
guardians of shared resources. In this case, one or more resources that are required by multiple concurrent
activities are encapsulated within an active class. By virtue of their built-in mutual exclusion semantics, such
guardians automatically protect these resources against concurrency conflicts.
For real-time systems, capsules should be used in place of active classes: wherever you identified the need for an
active class according to the heuristics described above, a capsule should be substituted.
Identify Subsystems. When the analysis class is complex, such that it appears to embody behaviors that cannot be
the responsibility of a single class acting alone, the analysis class should be mapped to a design subsystem. The
design subsystem is used to encapsulate these collaborations in such a way that clients of the subsystem can be
completely unaware of the internal design of the subsystem, even as they use the services provided by the subsystem.
A subsystem is modeled as a UML component, which has only interfaces as public elements. The interfaces provide a layer
of encapsulation, allowing the internal design of the subsystem to remain hidden from other model elements. The concept
subsystem is used to distinguish it from packages, which are semantic-free containers of model elements.
The decision to create a subsystem from a set of collaborating analysis classes is based largely on whether the
collaboration can be or will be developed independently by a separate design team. If the collaborations can be
completely contained within a package along with the collaborating classes, a subsystem can provide a stronger form of
encapsulation than that provided by a simple package. The contents and collaborations within a subsystem are completely
isolated behind one or more interfaces, so that the client of the subsystem is only dependent upon the interface. The
designer of the subsystem is then completely isolated from external dependencies; the designer (or design team) is
required to specify how the interface is realized, but they are completely free to change the internal subsystem design
without affecting external dependencies. In large systems with largely independent teams, this degree of de-coupling
combined with the architectural enforcement provided by formal interfaces is a strong argument for the choice of
subsystems over simple packages. See Guideline: Design Subsystem for more information about the factors which affect the choice to use subsystems as design elements.
|
Identify Subsystem Interfaces
Purpose
|
To identify the design elements which formalize the seams in the system.
|
Interfaces define a set of operations which are realized by some classifier. In the Design Model, interfaces are
principally used to define the interfaces for subsystems. This is not to say that they cannot be used for classes as
well, but for a single class it is usually sufficient to define public operations on the class which, in effect, define
its 'interface'. Interfaces are important for subsystems because they allow the separation of the declaration of
behavior (the interface) from the realization of behavior (the specific classes within the subsystem which realize the
interface). This de-coupling provides us with a way to increase the independence of development teams working on
different parts of the system, while retaining precise definitions of the 'contracts' between these different parts.
For each subsystem, identify a set of candidate interfaces. Using the grouped collaborations identified in the
previous step, identify the responsibility which is 'activated' when the collaboration is initiated. This
responsibility is then refined by determining what information must be provided by the 'client' and what information is
returned when the collaboration is complete; these sets of information become the prototype input and output parameters
and return value for an operation which the subsystem will realize. Define a name for this operation, using the naming
conventions defined in the Artifact: Project-Specific Guidelines. Repeat this until all
operations which will be realized by the subsystem have been defined.
Next, group operations together according to their related responsibilities. Smaller groups are preferable to larger
groups, since it is more likely that a cohesive set of common responsibilities will exist if there are fewer operations
in the group. Keep an eye toward reuse as well - look for similarities that may make it easier to identify related
reusable functionality. At the same time, though, don't spend a great deal of time trying to find the ideal grouping of
responsibilities; remember, this is just a first-cut grouping and refinement will proceed iteratively throughout the
elaboration phase.
Look for similarities between interfaces. From the candidate set of interfaces, look for similar names, similar
responsibilities, and similar operations. Where the same operations exist in several interfaces, re-factor the
interfaces, extracting the common operations into a new interface. Be sure to look at existing interfaces as well,
re-using them where possible. The goal is to maintain the cohesiveness of the interfaces while removing redundant
operations between interfaces. This will make the interfaces easier to understand and evolve over time.
Define interface dependencies. The parameters and return value of each interface operation each have a
particular type: they must realize a particular interface, or they must be instances of a simple data type. In cases
where the parameters are objects that realize a particular interface, define dependency relationships between the
interface and the interfaces on which it depends. Defining the dependencies between interfaces provides useful coupling
information to the software architect, since interface dependencies define the primary dependencies between elements in
the design model.
Map the interfaces to subsystems. Once interfaces have been identified, create realization associations
between the subsystem and the interfaces it realizes. A realization from the subsystem to an interface indicates that
there are one or more elements within the subsystem that realize the operations of the interface. Later, when the
subsystem is designed, these subsystem-interface realizations will be refined, with the subsystem designer specifying
which specific elements within the subsystem realize the operations of the interface. These refined realizations are
visible only to the subsystem designer; from the perspective of the subsystem client, only the subsystem-interface
realization is visible.
Define the behavior specified by the interfaces. Interfaces often define an implicit state machine for the
elements that realize the interface. If the operations on the interface must be invoked in a particular order (e.g. the
database connection must be opened before it can be used), a state machine that illustrates the publicly visible (or
inferred) states that any design element that realizes the interface must support should be defined. This state machine
will aid the user of the interface to better understand the interface, and will aid the designer of elements which
realize the interface to provide the correct behavior for their element.
Package the interfaces. Interfaces are owned by the software architect; changes to interfaces are always
architecturally significant. To manage this, the interfaces should be grouped into one or more packages owned by the
software architect. If each interface is realized by a single subsystem, the interfaces can be placed in the same
package with the subsystem. If the interfaces are realized by more than one subsystem, they should be placed within a
separate package owned by the software architect. This allows the interfaces to be managed and controlled independently
of the subsystems themselves.
Purpose
|
To identify the design elements which formalize the seams in the system (RT design only).
|
Protocols are similar to interfaces in event-driven systems: they identify the 'contract' between capsules by defining
a matched set of signals which are used to communicate between independent threads of control. While interfaces are
primarily used to define synchronous messaging using a function call model of invocation, protocols are primarily used
to define asynchronous communication using signal-based messaging. Protocols allow the separation of the declaration of
behavior (the set of signals) from the realization of behavior (the elements within the subsystem which realize the
interface). This de-coupling provides us with a way to increase the independence of development teams working on
different parts of the system, while retaining precise definitions of the 'contracts' between these different parts.
For each capsule, identify a set of in and out signals. Using the grouped collaborations identified in earlier
steps, identify the responsibility which is 'activated' when the collaboration is initiated. This responsibility is
then refined by determining what information must be provided by the 'client' and what information is returned when the
collaboration is complete; these sets of information become the prototype input parameters for a signal which the
capsule will realize through one of its ports. Define a name for this signal, using the naming conventions defined in
the Artifact: Project-Specific Guidelines. Repeat this until all signals
which will be realized by the capsule have been defined.
Next, group signals together according to their related responsibilities. Smaller groups are preferable to larger
groups, since it is more likely that a cohesive set of common responsibilities will exist if there are fewer signals in
the group. Keep an eye toward reuse as well - look for similarities that may make it easier to identify related
reusable functionality. At the same time, though, don't spend a great deal of time trying to find the ideal grouping of
responsibilities; remember, this is just a first-cut grouping and refinement will proceed iteratively throughout the
elaboration phase. Give the protocol a meaningful name, one that describes the role the protocol plays in capsule
collaborations.
Look for similarities between protocols. From the candidate set of protocols, look for similar names, similar
responsibilities, and similar signals. Where the same signals exist in several protocols, re-factor the protocols,
extracting the common signals into a new interface. Be sure to look at existing protocols as well, re-using them where
possible. The goal is to maintain the cohesiveness of the protocols while removing redundant signals between protocols.
This will make the protocols easier to understand and evolve over time.
Map the protocols to capsules. Once protocols have been identified, create ports on the capsules which
realize the protocols. The ports of the capsule define its 'interfaces', the behavior that can be requested from the
capsule. Later, when the capsule is designed, the behavior specified by the ports will be described by the state
machine for the capsule.
Define the behavior specified by the protocols. Protocols often define an implicit state machine for the
elements that realize the interface. If the input signals on the interface must be received in a particular order (e.g.
a 'system-ready' signal must be received before a particular error signal can be received), a state machine that
illustrates the publicly visible (or inferred) states that any design element that realizes the protocol must support
should be defined. This state machine will aid the user of the capsules which realize the protocol to better understand
their behavior, and will aid the designer of capsules to provide the correct behavior for their element.
Package the protocols. Protocols are owned by the software architect; changes to protocols are always
architecturally significant. To manage this, the protocols should be grouped into one or more packages owned by the
software architect. This allows the protocols to be managed and controlled independently of the capsules which realize
the protocols.
|
|
Properties
Multiple Occurrences | |
Event-Driven | |
Ongoing | |
Optional | |
Planned | |
Repeatable | |
More Information
© Copyright IBM Corp. 1987, 2006. All Rights Reserved.
|
|