0% found this document useful (0 votes)
2 views58 pages

10 Object Design

The document outlines the phases of Object-Oriented Analysis and Design, focusing on the transition from analysis to design, where the analysis model is fleshed out for implementation. It describes the steps involved in object design, including combining models, designing algorithms, and optimizing data structures for efficiency. Additionally, it emphasizes the importance of assigning responsibilities for operations and optimizing the design for clarity and performance.

Uploaded by

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

10 Object Design

The document outlines the phases of Object-Oriented Analysis and Design, focusing on the transition from analysis to design, where the analysis model is fleshed out for implementation. It describes the steps involved in object design, including combining models, designing algorithms, and optimizing data structures for efficiency. Additionally, it emphasizes the importance of assigning responsibilities for operations and optimizing the design for clarity and performance.

Uploaded by

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

Object-Oriented

Analysis and Design

10 OBJECT
OBJECT
DESIGN
DESIGN

Source:OBJECT-ORIENTED
MODELING AND DESIGN
James Rumbaugh, Michael Blaha,
William Premerlani, Frederick Eddy,
William Lorensen

PAUL VOUGNY - EPITA 2008


✘ The analysis phase determines what the implementation must do, and the system
design phase determines the plan of attack.

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

✘ In particular, the operations identified during analysis must be expressed as


algorithms, with complex operations decomposed into simpler internal operations.

✘ The classes, attributes, and associations from analysis must be implemented as


specific data structures.

✘ New object classes must be introduced to store intermediate results during


program execution and to avoid the need for recomputation.
Working from Analysis and Architecture

✘ The object model describes:


- classes of objects in the system
- attributes
- operations that they support

Object design then becomes a process of adding detail and making


implementation decisions.

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.

The flow of control within a program must be realized:

- either explicitly: by an internal scheduler that recognizes events and maps them
into operation calls.

- or implicitly: by choosing algorithms that perform the operations in the order


specified by the dynamic model.

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:

✘ Combine the three models to obtain operations on classes

✘ Design algorithms to implement operations

✘ Optimize access paths to data

✘ Implement control for external interactions

✘ Adjust class structure to increase inheritance

✘ Design associations

✘ Determine object representation Many of the examples in this chapter


come from the design of the
✘ Package classes and associations Object Modeling Tool (OMTool), a
into modules graphic editor for constructing object
diagrams.
2. COMBINING THE THREE MODELS
The designer must convert the actions and activities of the dynamic model and the
processes of the functional model into operations attached to classes in the object
model.

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.

Therefore the algorithm implementing an operation depends on the state of the


object.

An event sent by an object may reprensent an operation on another object.


The network of processes within the data flow diagram represents the body of an
operation.

The flows in the diagram are intermediate values in the operation.

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.

The algorithm designer must:

✘ Choose algorithms that minimize the cost of implementing operations

✘ Select data structures appropriate to the algorithms

✘ Define new internal classes and operations as necessary

✘ Assign responsibility for operations to appropriate classes


Choosing Algorithms

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.

Many operations simply traverse paths in the object-link network to retrieve or


change attributes or links.

Class box Operation list Operation


box operations list entry entry

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.

For example, searching for a value in a


In other cases, the simple set of size n by scanning the set
definition of an operation would requires an average of n/2 operations,
be hopelessly inefficient and must whereas a binary search takes log n
be implemented with a more operations and a hash search takes
efficient algorithm. less than 2 operations on average,
regardless of the size of the set.

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.

For example, the infamous "bubble sort"


algorithm requires time proportional to n2, where
n is the size of the list, while most alternative sort
algorithms require time proportional to n log n.

✘ Ease of implementation and understandability


It is worth giving up some performance on noncritical operations if they can be
implemented quickly with a simple algorithm.

For example, a pick operation. Here the speed is


not a problem because the operation is only
performed when the user pushes a button.
✘ Flexibility
A highly optimized algorithm often sacrifies readability and ease of change.

One possibility is to provide two implementations of critical operations:

- a simple but inefficient algorithm that can be implemented quickly and used to
validate the system

- a complicated but efficient algorithm, whose correct implementation can be


checked against the simple one.

For example, a more complicated algorithm for


picking objects in a diagram could be
implemented by spatially sorting all the objects in
a large, flat data structure.
✘ Fine tuning the object model
If the object model were structured differently, would there be other alternative?

Inefficient because operations on a


Diagram
Window set of elements must be computed
element visible
separately for each window.

Each element belongs to one


sheet, which may appear in any
Diagram number of windows.
Sheet Window
element sheet visible The image on the sheet can be
computed once, and then copied to
each window as a bitmap
operation.
Alternative
structure

The reduction in repeated operations is worth the extra level of indirection.


Choosing Data Structures

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.

Such data structures include arrays, lists,


queues, stacks, sets, bags, dictionaries,
associations, trees, and many variations on
these, such as priority queues and binary trees.

Most object-oriented languages provide an assortiment of generic data structures as


part of their predefined class libraries.
Defining Internal Classes and Operations

During the expansion of algorithms, new classes of objects may be needed to hold
intermediate results.

A complex operation can be defined in terms of lower-level operations on simpler


objects.

Some of the required lower-level operations may be found among the


"shopping-list" operations.

For example, the OMTool erase operation on a diagram element


is conceptually simple, but its implementation on a pixel-based
screen is more complicated. To erase an object, it must be drawn
in the background color, then objects uncovered by the erasure or
damaged by the draw must be repaired by being redrawn. The
repair operation is purely an internal operation needed because we
are working on a pixel-based screen.
When you reach this point during the design phase, you may have to add new
classes that were not mentioned directly in the client's description of the problem.

These low-level classes are the implementation elements out of which the
application classes are built.

For example, an OMTool class box image is


made of rectangles, lines, and text strings in
various fonts. In OMTool, the low-level graphics
elements come from the graphics toolkit module,
which supplies its services to the rest of the
system. Typically low-level implementation
classes are placed in a distinct module.
Assigning Responsibility for Operations
Many operations have obvious target objects, but some operations can be
performed at several places in an algorithm, by one of several objects, as long as
they eventually get done.

For example, an OMTool has a drag operation applicable to diagram elements.


Dragging a box moves the box and all the lines attached to it, provided nothing pushes
up against an obstacle. The drag operation propagates from object to object along
connections between objects.

Some drags can fail because of obstacles, and backtracking may be required.
Eventually the picture must be redrawn on the screen.

● When should each object image be redrawn on the screen?


● After it is dragged by another object?
● After it drags other objects?
● After all objects have been dragged?
● Is each object responsible for redrawing itself, or is the entire picture responsible
for redrawing itself?

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.

Ask yourself the following questions:

✘ 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?

Assigning an operation to a class within a generalization hierarchy can sometimes


be difficult because the definitions of the subclasses within the hierarchy are often
fluid and can be adjusted during design as convenient.

It is common to move an operation up and down in the hierarchy during design, as


its scope is adjusted.
4. DESIGN OPTIMIZATION
The designer must strike an appropriate balance between efficiency and clarity.

During optimization, the designer must:

✘ Add redundant associations to minimize access cost and maximize convenience

✘ Rearrange the computation for greater efficiency

✘ Save derived attributes to avoid recomputation of complicated expressions


Adding Redundant Associations for Efficient Access

During analysis, it is undesirable to have redundancy in the association network


because redundant associations do not add any information. During design,
however, we evaluate the structure of the object model for an implementation.

Is there a specific arrangement of the network that


would optimize critical aspects of the completed
system?

Should the network be restructured by adding new


associations?

Can existing associations be omitted?


To demonstrate the analysis of access paths, consider the design of a
company's employee skills database.

The operation Company::find_skill returns a set of persons in the


company with a given skill. For example, we might ask for all employees
who speek Japanese.

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

Speaks language Index for


Company language Person personal skills
database

The derived association does not


add any information to the
network but permits the model
information to be accessed in a
more efficient manner.
Rearranging Execution Order for Efficiency

After adjusting the structure of the object model to optimize frequent traversals, the
next thing to optimize is the algorithm itself.

One key to algorithm optimization is to eliminate dead paths as early as possible.

For example, suppose we want to find all employees who speak


both Japanese and French. Suppose 5 employees speak
Japanese and 100 speak French; it is better to test and find the
Japanese speakers first, then test if they speak French.
Saving Derived Attributes to Avoid Recomputation

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.

Attribute {ordered} Attribute


Attribute list text
{ordered}
Class text Class region /location
box box
location Operation location Operation Operation
{ordered} text list text
region {ordered} /location

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.

The Overlaps association stores those elements that overlap


an object and precede it in the list. This association must be
updated when a new element is added, but testing for overlap
using the association is more efficient.

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.

There are three basic approaches to implementing the dynamic model:

✘ Using the location within the program to hold state (procedure-driven system)

✘ Direct implementation of a state machine mechanism (event-driven system)

✘ Using concurrent tasks


State as Location within a Program (procedure-driven system)

One technique of converting a state diagram to code is as follows:

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:

First, we identify the main path of control, which corresponds to


- the reading of a card
- querying the user for transaction information
- processing the transaction
- printing a receipt
- ejecting the card.

Alternative flows of control occur if the customer wants to


process more than one transaction or if the password is bad
and the customer is asked to try again.
Main screen
insert card Pseudocode

Request password do forever


display main screen
enter password read card
repeat
Verify account ask for password
bad account read password
account OK verify account
until account verification is OK
Request kind repeat
repeat
enter kind ask for kind of transaction
read kind
Request amount ask for amount
read amount
enter account start transaction
wait for it to complete
Process transaction until transaction is OK
transaction
dispense cash
failed transaction succeed wait for customer to take it
ask whether to continue
Dispense cash until user asks to terminate
take cash eject card
wait for customer to take card
Request continuation
continue
terminate
ATM The cancel event cound be added to
Finish
control
the flow control and implemented as
Card ejected goto exception handling code.
take card
State Machine Engine (event-driven system)

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.

A stub is the minimal definition of a function or subroutine without any internal


code (code to return a precalculated or contrived value may be included).

A parser, such as Unix yacc or Lex, produces an explicit state machine to


implement a user interface. Some application packages, especially in the
user interface area, permit state machines to be supplied as tables to be
interpreted by the package.
Control as Concurrent Tasks

An object can be implemented as a task in the programming language or operating


system.

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.

The designer should:

✘ Rearrange and adjust classes and operations to increase inheritance

✘ Abstract common behavior out of groups of classes

✘ Use delegation to share behavior when inheritance is semantically invalid


Rearranging Classes and Operations

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.

✘ An operation may be defined on several different classes in a group but be


undefined on the other classes. Define it on the common ancestor class and
declare it as a no-op on the classes that don't care about it.
Abstracting Out Common Behavior

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.

This transformation of the object model is called abstracting out a common


superclass or common behavior.

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.

The creation of abstract superclasses also improves the extensibility of a software


product.
Generating customized versions of your software to match each different
configuration could be tedious.

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

Sharing of behavior is justifiable only when a true generalization relationship occurs,


that is, only when it can be said that the subclass is a form of the superclass.

Operations of the subclass that override the corresponding operation of the


superclass have a responsibility to provide the same services provided by the
superclass, and possibly more.

When class B inherits the specification of class A,


we can assume that every instance of class B is an
instance of class A because it behaves the same.

Sometimes programmers use inheritance as an implementation technique with no


intention of guaranteeing the same behavior.

This can lead to problems if other operations that are inherited provide unwanted
behavior.

We discourage this inheritance of implementation because it can lead to incorrect


behavior.
The ability to corrupt the stack by adding or
removing arbitrary elements is hidden from the client
of the Stack class.

List Stack List


body: list {private}
add push add Alternative
remove pop remove implementations
first first of a Stack
last last

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 an association is only traversed in one direction, it may be implemented as a


pointer: an attribute which contains an object reference.

- If the object multiplicity is "one", then it is a simple pointer.


- If the multiplicity is "many", then it is a set of pointers.

If the "many" end is ordered, then a list can be used instead of a set.

A qualified association with multiplicity "one" can be implemented as a dictionary


object. A dictionary is a set of value pairs that maps selector values into target
values. Dictionaries are implemented efficiently in most object-oriented languages
using hashing.

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:

✘ Implement as an attribute in one direction only and perform a search when a


backward traversal is required.

This approach is useful only if there is a great disparity in traversal frequency in


the two directions and minimizing both the storage cost and the update cost are
important. The rare backward traversal will be expensive.
✘ Implement as attributes in both directions, using the techniques outlined in the
previous section and shown here.

This approach permits fast access, but if either attribute is updated then the other
attribute must also be updated to keep the link consistent.

This approach is useful if accesses outnumber updates.

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.

An association object is a set of pairs of associated objects (triples for qualified


associations) stored in a single variable-size object.

For efficiency, an association object can be implemented


using two dictionary objects:
- one for the forward direction Implementation
of association
- one for the backward direction.
as an object

Works_for
(Person) (Company)

(Person)

(Person)

(Person)
(Company)

(Person)
Link Attributes

If an association has link attributes, then its implementation depends on the


multiplicity.

✘ If the association is one-to-one, the link attributes can be stored as attributes of


either object.

✘ If the association is many-to-one, the link attributes can be stored as attributes


of the "many" object, since each "many" object appears only once in the
association.

✘ If the association is many-to-many, the link attributes cannot be associated with


either object; the best approach is usually to implement the association as a
distinct class, in which each instance represents one link and its attributes.
8. OBJECT REPRESENTATION
Classes can be defined in terms of other classes, but eventually everything must be
implemented in terms of built-in primitive data types, such as intergers, strings,
and enumerated types.

Employee Employee SSN SS Number


SSN: integer SSN: integer Alternative
representations
for an attribute
Employee Employee SSN SS Number
SSN: string SSN: string

Line Line p1 Point In a similar vein, the designer must


x1: real x: real often choose whether to combine
y1: real p2 y: real groups of related objects.
x2: real
y2: real
Embedded
Neither representation is inherently
and explicit superior because both are
objects mathematically correct.
9. PHYSICAL PACKAGING
Programs are made of discrete physical units that can be edited, compiled,
imported, or otherwise manipulated.

Packaging involves the following issues:

✘ Hiding internal information from outside view

✘ Coherence of entities

✘ Constructing physical modules


Information Hiding

✘ One design goal is to treat classes as "black boxes", whose external interface is
public but whose internal details are hidden from view.

Hiding internal information permits implementation of a class to be changed


without requiring any clients of the class to modify code.

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:

● Allocate to each class the responsibility of performing operations and


providing information that pertains to it.

● Call an operation to access attributes belonging to an object of another


class.

● Avoid traversing associations that are not connected to the current class.

● Define interfaces at as high a level of abstraction as possible.

● Hide external objects at the system boundary by defining abstract


interface classes, that is, classes that mediate between the system and
the raw external objects.

● Avoid applying a method to the result of another method, unless the


result class is already a supplier of methods to the caller. Instead
consider writing a method to combine the two operations.
Coherence of Entities
✘ One important design principle is coherence of entities.

An entity, such as a class, an operation, or a module, is coherent if it is


organized on a consistent plan and all its part fil together toward a common goal.

An entity should have a single major theme; it should not be a collection of


unrelated parts.

✘ A single method should not contain both policy and implementation.

● Policy is the making of context-dependent decisions.


● Implementation is the execution of fully-specified algorithms.

Policy involves making decisions, gathering global information, interacting with


the outside world, and interpreting special cases. A policy method contains I/O
statements, conditionals, and accesses data stores. A policy method does not
contain complicated algorithm but instead calls various implementation methods.

An implementation method does exactly one operation, without making any


decisions, assumptions, defaults, or deviations. All its information is supplied as
arguments, so the argument list may be long.
✘ Separating policy and implementation greatly increases the possibility of
reusability.

The implementation methods do not contain any context dependencies, so they


are likely to be reusable.

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

Try to encapsulate strong coupling within a single module.


10. DOCUMENTING DESIGN DECISIONS
The design decisions discussed in this chapter must be documented when they are
made, or you will become confused.

This is especially true if you are working with other developers.

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.

The Design Document should be an extension of the Requirements Analysis


Document.

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.

It is particulary important to specify all operation interfaces by giving their


arguments, results, input-output mappings, and side effects.

Traceability from an element in the original analysis to the corresponding element in


the design document should be straightforward since the design document is an
evolution of the analysis model and retains the same names.

You might also like