10 Object Design
10 Object Design
10 OBJECT
OBJECT
DESIGN
DESIGN
Source:OBJECT-ORIENTED
MODELING AND DESIGN
James Rumbaugh, Michael Blaha,
William Premerlani, Frederick Eddy,
William Lorensen
✘ The object design phase determines the full definitions of the classes and
associations used in implementation, as well as the interfaces and algorithms of
the methods used to implement operations.
✘ This chapter shows how to take the analysis model and flesh it out to provide a
basis for implementation. In the OMT methodology, there is no need to transform
from one model to another, as the object-oriented paradigm spans analysis,
design, and implementation.
1. OVERVIEW OF OBJECT DESIGN
✘ During object design the designer carries out the strategy chosen during system
design and fleshes out the details.
✘ The objects discovered during analysis serve as the skeleton of the design, but the
object designer must choose among different ways to implement them with an eye
toward minimizing execution time, memory, and other measures of cost.
Occasionally, an analysis object does not appear explicitly in the design but is
distributed among other objects for computational efficiency. More often, new
redundant classes are added for efficiency.
✘ The functional model describes the operations that the system must implement.
During design we must decide how each operation should be implemented,
choosing an algorithm for the operation and breaking complex operations into
simpler operations.
✘ The dynamic model describes how the system responds to external events.
- either explicitly: by an internal scheduler that recognizes events and maps them
into operation calls.
If multiple processors are involved, we have decided how objects will be allocated
to processors. The choice of architecture will also influence the decision of how to
map events into operations.
Steps of Object Design
During object design, the designer must perform the following steps:
✘ Design associations
In making this conversion, we begin the process of mapping the logical structure of
the analysis model into a physical organization of a program.
In the state diagram, the action performed by a transition depends on both the
event and the state of the object.
The designer must convert the graph structure of the diagram into a linear sequence
of steps in an algorithm.
The processes in the data flow diagram constitute suboperations. Some of them, but
not necessarily all, may be operations on the original target object or on other
objects.
Determine the target object of a suboperation as follow:
✘ If a process extracts a value from an input flow, then the input flow is the target.
✘ If a process has an input flow and an output flow of the same type, and the output
value is substantially an updated version of the input flow, then the input/output
flow is the target.
✘ If a process constructs an output value from several input flows, then the
operation is a class operation (constructor) on the output class.
✘ If a process has an input from or an output to a data store or an actor, then the
data store or actor is a target of the process. In some cases, such a process must
be broken into two operations, one for the actor or data store and one for a flow
value.
The original target class is a client of any classes that supply internal operations to
one of its operations.
3. DESIGNING ALGORITHMS
Each operation specified in the functional model must be formulated as an
algorithm.
The analysis specification tells what the operation does from the view point of its
clients, but the algorithm shows how it is done.
Many operations are simple enough that the specification in the functional model
already constitutes a satisfactory algorithm because the description of what is done
also shows how it is done.
box
Attribute list Attribute
attributes list entry entry
Fragment of
OMTool
model
For example, "the circle passing
through three noncolinear points" is
Some functions are specified as a nonprocedural specification of a
declarative constraints, without circle. In such cases, you must use
any procedural definition. your own knowledge of the situation
(and appropriate reference books) to
invent an algorithm.
It is not necessary to write algorithms for trivial operations that are internal to one
object, such as setting or accessing the value of an attribute.
Considerations in choosing among alternative algorithms include:
✘ Computational complexity
It is essential to think about the complexity of the algorithm, that is, how the
execution time (or memory) grows with the number of input values (constant time,
linear, quadratic, or exponential) as well as the cost of processing each input value.
- a simple but inefficient algorithm that can be implemented quickly and used to
validate the system
The data structures do not add information to the analysis model, but they organize
it in a form convenient for the algorithms that use it. Many implementation data
structures are instances of container classes.
During the expansion of algorithms, new classes of objects may be needed to hold
intermediate results.
These low-level classes are the implementation elements out of which the
application classes are built.
Some drags can fail because of obstacles, and backtracking may be required.
Eventually the picture must be redrawn on the screen.
Such questions are hard to answer because the breakdown on a complex externally
meaningful operation into internal operations is arbitrary.
How do you decide what class owns an operation?
When only one object is involved in the operation, the decision is easy: ask (or tell)
that object to perform the operation.
The decision is more difficult when more that one object is involved in an operation.
You must decide which object plays the lead role in the operation.
✘ Is one object acted on while the other object performs the action? In general, it is
best to associate the operation with the target of the operation, rather than the
initiator.
✘ Is one object modified by the operation, while other objects are only queried for
the information they contain? The object that is changed is the target of the
operation.
✘ Looking at the classes and associations that are involved in the operation, which
class is the most centrally-located in this subnetwork of the object model? If the
classes and associations form a star about a single central class, it is the target of
the operation.
✘ If the objects were not software, but were the real-world object being represented
internally, what real object would you push, move, activate, or otherwise
manipulate to initiate the operation?
Employs Has_skill
Company Person Skill
Chain of
associations
We can add a qualified association Speaks language from Company to
Employee, where the qualifier is the language spoken. This permits us to
immediately access all employees who speak a particuliar language with no
wasted accesses.
derived
association
After adjusting the structure of the object model to optimize frequent traversals, the
next thing to optimize is the algorithm itself.
Data that is redundant because it can be derived from other data can be "cached" or
stored in its computed form to avoid the overhead of recomputing it.
New objects or classes may be defined to retain this information. The class that
contains the cached data must be updated if any of the objects that it depends on
are changed.
Derived attribute
to avoid
recomputation
A sheet contains a priority list of partially overlapping elements.
If an element is moved or deleted, the elements under it must
be redrawn.
next Priority
list
Diagram
over
element previous
Overlaps
under
Association
as a cache
Derived attributes must be updated when base values change. There are three ways
to recognize when an update is needed:
✘ Explicit update
The designer determines which derived attributes are affected by each change to a
fundamental attribute and inserts code into the update operation on the base object
to explicitly update the derived attributes that depend on it.
✘ Periodic recomputation
Base values are often updated in bunches. Sometimes it is possible to simply
recompute all the derived attributes periodically without recomputing derived
attributes after each base value is changed.
✘ Active values
An active value is a value that has dependent values. Each dependent value
registers itself with the active value, which contains a set of dependent values and
update operations. An operation to update the base value triggers updates of all the
dependent values, but the calling code need not explicitly invoke the updates.
5. IMPLEMENTATION OF CONTROL
The designer must refine the strategy for implementing the state-event models
present in the dynamic model.
✘ Using the location within the program to hold state (procedure-driven system)
1. Identify the main control path. Beginning with the initital state, identify a path
through the diagram that corresponds to the normally expected sequence of
events. Write the names of states along this path as a linear sequence. This
becomes a sequence of statements in the program.
2. Identify alternate paths that branch off the main path and rejoin it later. These
become conditional statements in the program.
3. Identify backward paths that branch off the main loop and rejoin it earlier. These
become loops in the program. If there are multiple backward paths that do not
cross, they become nested loops in the program. Backward paths that cross do
not nest and can be implemented with gotos if all else fails, but these are rare.
4. The states and transitions that remain correspond to exception conditions. They
can be handled by several techniques including error subroutines, exception
handling supported by the language, or setting and testing of status flags.
Exception handling is a legitimate use for gotos in a programming language
because their use frequently simplifies breaking out of a nested structure, but do
not use them unless necessary.
State model for the ATM class:
For example, a general "state machine engine" class could provide the
capability to execute a state machine represented by a table of transitions
and actions provided by the application.
Each object instance would contain its own independent state variables
but would call on the state engine to determine the next state and action.
This approach allows you to quickly progress from the analysis model to a skeleton
prototype of the system by defining classes from the object model, state machines
from the dynamic model, and creating "stubs" of the action routines.
Events are implemented as inter-task calls using the facilities of the language or
operating system.
As in the previous implementation, the task uses its location within the program to
keep track of its state.
6. ADJUSTMENT OF INHERITANCE
As object design progresses, the definitions of classes and operations can often be
adjusted to increase the amount of inheritance.
Often operations in different classes are similar but not identical. By slightly
modifying the definitions of the operations or the classes, the operations can often
be made to match so that they can be covered by a single inherited operation.
Before inheritance can be used, each operation must have the same interface and
the same semantics.
All operations must have the same signature, that is, the same number and types of
arguments and results.
The following kinds of adjustments can be used to increase the chance of
inheritance:
✘ Some operations may have fewer arguments than others. The missing arguments
can be added but ignored.
✘ Some operations may have fewer arguments because they are special cases of
more general arguments. Implement the special operations by calling the general
operation with appropriate parameter values.
✘ Similar attributes in different classes may have different names. Give the attributes
the same name and move them to a common ancestor class. Then operations that
access the attributes will match better. Also watch for similar operations with
different names. A consistent naming strategy is important to avoid hiding
similarities.
When common behavior has been recognized, a common superclass can be created
that implements the shared features, leaving only the specialized features in the
subclasses.
Usually the resulting superclass is abstract, meaning that there are no direct
instance of it, but the behavior it defines belongs to all instances of its subclasses.
Sometimes it is worthwhile to abstract out a superclass even when there is only one
subclass in your project that inherits from it. Although this does not result in any
sharing of behavior in the immediate project, the abstract superclass thus created
may be reusable in future projects.
Abstract superclasses have benefits other than sharing and reuse. The splitting of a
class into two classes that separate the specific aspects from the more general
aspects is a form of modularity.
You distribute one version of software that contains a subclass for each
known model of sensor.
When the software starts up, it reads a configuration file provided by the
customer that tells it which model of sensor is used in which location and
creates an instance of the particular subclass that handles that type of
sensor.
All the rest of the code treats the sensors as if they were all the same as
defined by the Sensor superclass.
Use Delegation to Share Implementation
This can lead to problems if other operations that are inherited provide unwanted
behavior.
Stack
push
pop Reserve the use of
inheritance for cases where
inheritance delegation
you can say that an instance
Discouraged Recommended
of one class also is an
instance of some other class.
7. DESIGN OF ASSOCIATIONS
Analyzing Association Traversal
We have assumed until now that associations are inherently bidirectional, which is
certainly true in an abstract sense.
But if some associations in your application are only traversed in one direction, their
implementation can be simplified.
Whichever implementation strategy you choose, you should hide the implementation
using access operations to traverse and update the association. This will allow you
to change your decision with minimal effort.
One-way Associations
If the "many" end is ordered, then a list can be used instead of a set.
Qualified associations with multiplicity "many" are rare, but they can be
implemented as a dictionary of sets of objects.
Works_for
Person Company
Implementation
of one-way
association
Person Company
using pointers
employer employees
Two-way Associations
Many associations are traversed in both directions, although not usually with equal
frequency. There are three approaches to their implementation:
This approach permits fast access, but if either attribute is updated then the other
attribute must also be updated to keep the link consistent.
Works_for
Person Company
Person Company
employer employees
Implementation
of two-way
Set
association
using pointers
✘ Implement as a distinct association object, independent of either class.
Works_for
(Person) (Company)
(Person)
(Person)
(Person)
(Company)
(Person)
Link Attributes
✘ Coherence of entities
✘ One design goal is to treat classes as "black boxes", whose external interface is
public but whose internal details are hidden from view.
Furthermore, additions and changes to the class are surrounded by "fire walls"
that limit the effects of any changes so that changes can be understood clearly.
✘ During analysis, we were not concerned with information hiding. During design,
however, the public interface of each class must be carefully defined.
The designer must decide what attributes should be accessible from outside the
class.
✘ Taken to an extreme, a method on a class could traverse all the associations of
the object model to locate and access another object in the system.
But methods that know too much about the entire model are fragile because any
change in representation invalidates them.
We need to define the bounds of visiblity that each method requires. Specifying
what other classes a method can see defines the dependencies between classes.
✘ Each operation should have a limited knowledge of the entire model, including the
structure of classes, associations, and operations.
The fewer things that an operation knows about, the less likely it will be affected
by any changes.
Conversely, the fewer operations know about details of a class, the easier the class
can be changed if needed.
The following design principles help to limit the scope of knowledge of any
operation:
● Avoid traversing associations that are not connected to the current class.
✘ Policy methods are less likely to be reusable, but they are not usually as
complicated because they do not contain computational algorithms.
✘ A class should not serve too many purposes at once. If it is too complicated, it can
be broken up using either generalization or aggregation.
Smaller pieces are more likely to be reusable than large complicated pieces.
Constructing Modules
The new classes that we have added during design either add to an existing module
or layer or can be organized into a separate module or layer that did not exist in the
analysis.
A rough rule of thumb is that classes that are closely connected by associations
should be in the same module, while classes that are not connected, or are loosely
connected may be in separate modules.
It is impossible to remember design details for any nontrivial software system, and
documentation is often the best way of transmitting the design to others and
recording it for reference during maintenance.
Thus the Design Document will include a revised and much more detailed
description of the Object Model, in both graphical form (object model diagrams)
and textual form (class descriptions).
Additional notation is appropriate for showing implementation decisions, such as
arrows showing the traversal direction of associations and pointers from attributes
to other objects.