848 Patterns and UML
Self-Test Exercises
The Divide-and-Conquer Sorting pattern must divide the interval being sorted into two smaller
intervals. If there were cases in which the
split function divided the interval into an empty
interval and the rest, then one subinterval would be the same as the original interval being
divided, and infinite recursion would result. In the quick-sort realization we avoided this infinite
recursion with the following lines from the definition of
split:
if (up > 0)
return (begin + up);
else
return (begin + 1); //Ensures that both pieces are nonempty.
Without this extra adjustment, the function split could compute 0 as the value of up and so
divide the interval into two intervals with one of the two being empty. That would produce infi-
nite recursion. The way this is usually avoided with a quick-sort realization (and the way that pro-
duces the nicest code) is to separate out the split point and divide only the remaining element, so
that the array interval is divided into three pieces: the split point, the subinterval before the split
point, and the subinterval after the split point. This guarantees that even if one subinterval is
empty, the other is shorter than the interval being divided. Thus, infinite recursion is avoided.
When these points are taken into consideration, you are likely to change the Sorting pattern to the
following when you are designing the quick-sort realization:
if ((end - begin) > 1)
{
int splitPt = split(a, begin, end);
sort(a, begin, splitPt - 1);
sort(a, splitPt + 1, end);
}//else sorting one (or fewer) elements, so do nothing.
Patterns are there to help you, not to provide an obstacle. Feel free to adjust them if need be.
■
PATTERN FORMALISM
A well-developed body of techniques exists for using patterns. We will not go into the
details here. The UML discussed in Section 20.2 is one formalism used to express pat-
terns. The place of patterns and any specific formalisms for patterns within the software
design process is not yet clear. However, it is clear that basic patterns—as well as certain
pattern names, such as Model-View-Controller—have become standard and useful tools
for software design.
1. Is a template function definition a pattern?
2. Give the contents of a file named
selectionsort.cpp that will realize the selection sort
algorithm (see Display 5.8) for the Divide-and-Conquer Sorting pattern given in Display
20.2. (This is the selection sort analog of what was done for the quick sort in Display 20.5.)
20_CH20.fm Page 848 Monday, August 18, 2003 2:08 PM
UML 849
3. Which of the following would give the fastest running time when an array is sorted using
the quick-sort algorithm: a fully sorted array, an array of random values, or an array sorted
from largest to smallest (that is, sorted backwards)? Assume that all arrays are of the same
size and have the same base type.
UML
One picture is worth a thousand words.
Chinese proverb
People do not think in C++ or in any other programming language. As a result, com-
puter scientists have always sought to produce more human-oriented ways of represent-
ing programs. One widely used representation is pseudocode, which is a mixture of a
programming language such as C++ and a natural language such as English. To think
about a programming problem without needing to worry about the syntax details of a
language such as C++, you can simply relax the syntax rules and write in pseudocode.
Pseudocode has become a standard tool used by programmers, but it is a linear and
algebraic representation of programming. Computer scientists have long sought to give
software design a graphical representation. To this end, a number of graphical represen-
tation systems for program design have been proposed, used, and ultimately found to
be wanting. Terms such as flowchart, structure diagram, and many other names of
graphical program representations are today only recognized by those of the older gen-
eration. Today’s candidate for a graphical representation formalism is the Unified
Modeling Language (UML). The UML was designed to reflect and be used with the
object-oriented programming philosophy. It is too early to say whether or not the
UML will stand the test of time, but it is off to a good start. A number of companies
have adopted the UML formalism for use in their software design projects.
■
HISTORY OF UML
UML developed along with object-oriented programming. As the OOP philosophy
became more and more commonly used, different groups developed their own graphi-
cal or other representations for OOP design. In 1996 Grady Booch, Ivar Jacobson, and
James Rumbaugh released an early version of UML. The UML was intended to bring
together the various different graphical representation methods to produce a standard-
ized graphical representation language for object-oriented design and documentation.
Since that time the UML has been developed and revised in response to feedback from
the OOP community. Today the UML standard is maintained and certified by the
Object Management Group (OMG), a nonprofit organization that promotes the use of
object-oriented techniques.
20.2
Unified
Modeling
Language
(UML)
20_CH20.fm Page 849 Monday, August 18, 2003 2:08 PM
850 Patterns and UML
■
UML CLASS DIAGRAMS
Classes are central to OOP, and the class diagram is the easiest of the UML graphical
representations to understand and use. Display 20.6 shows the class diagram for a class
to represent a square. The diagram consists of a box divided into three sections. The
top section has the class name,
Square. The next section has the data specification for
the class. In this example there are two pieces of data (two member variables): a value of
type
double giving the length of a side, and a value topRtCorner giving the location of
the top-right corner of the square. (The value
topRtCorner is given as a pair of num-
bers of type
double, which specify a point in x,y-coordinates relative to some origin.) A
minus sign indicates a private member. So, for the class
Square, all data is private. The
third section gives the actions (class member functions). A plus sign indicates a public
member. A sharp sign,
#, indicates a protected member. So for the class Square, the
class diagram shows two public member functions and one protected member func-
tion. A class diagram need not give a complete description of the class. When you do
not need all the members in a class for the analysis at hand, you do not list all the mem-
bers in the class diagram. Missing members are indicated with an ellipsis (three dots).
■
CLASS INTERACTIONS
Class diagrams by themselves are of little value, since they simply repeat the class inter-
face, possibly with ellipses. To understand a design, you need to indicate how objects of
the various classes interact. The UML has various ways to indicate class interactions.
Various sorts of annotated arrows indicate the information flow from one class object
to another, for example, as in Display 20.1. The UML also has annotations for class
groupings into library-like aggregates, for inheritance, and for other interactions.
Moreover, the UML is extensible. If what you want and need is not in the UML, you
class diagram
Display 20.6 A UML Class Diagram
+resize(double newSide): void
+move(Pair<double, double> point): void
#erase( ): void
-side: double
-topRtCorner: Pair<double, double>
Square
20_CH20.fm Page 850 Monday, August 18, 2003 2:08 PM
Answers to Self-Test Exercises 851
Self-Test Exercises
can add it to the UML. Of course, this all takes place inside a prescribed framework so
that different software developers can understand each other’s UML.
This is just a hint of what the UML is all about. If you are interested in learning
more, consult the references listed at the end of this book.
4. Draw a class diagram for a class whose objects represent circles. Use Display 20.6 as a
model.
5. Draw a class diagram for the
IntNode class presented in Display 17.4.
■ Patterns are design principles that apply across a variety of software applications.
■ The patterns discussed in this chapter are the Container-Iterator, Adapter, Model-
View-Controller, and Divide-and-Conquer Sorting patterns.
■ A pattern can give you a framework for comparing related algorithms for efficiency.
■ The Unified Modeling Language (UML) is a graphical representation language for
object-oriented software design.
■ UML is one formalism that can and is used to express patterns.
ANSWERS TO SELF-TEST EXERCISES
1. Yes, a template function definition is a pattern, but the term pattern is much more general and
encompasses more. Moreover, a useful pattern expressed as a template function would leave
some details unimplemented. If the only variation possible is a type parameter, it is still a pat-
tern but not likely to be usefully viewed as a pattern. (It can still be useful as a template func-
tion, but it might not be useful to view it as a pattern in the sense discussed in this chapter.)
2. //File selectionsort.cpp: the selection sort realization
//of the Sorting pattern.
#include <algorithm>
using std::swap;
template <class T>
int indexOfSmallest(const T a[], int startIndex,
int sentinelIndex)
{
int min = a[startIndex],
indexOfMin = startIndex;
for (int index = startIndex + 1;
index < sentinelIndex; index++)
Chapter Summary
20_CH20.fm Page 851 Monday, August 18, 2003 2:08 PM
852 Patterns and UML
if (a[index] < min)
{
min = a[index];
indexOfMin = index;
//min is the smallest of a[startIndex]
//through a[index]
}
return indexOfMin;
}
template <class T>
int split(T a[], int begin, int end)
{
int index = indexOfSmallest(a, begin, end);
swap(a[begin], a[index]);
return (begin + 1);
}
template <class T>
void join(T a[], int begin, int splitPt, int end)
{
//Nothing to do.
}
3. An array of random values would have the fastest running time, since it would divide the
array segments into approximately equal subarrays most of the time. The other two cases
would give approximately the same running time, which would be the worst-case O(N
2
)
running time because the algorithm would always divide an array segment into very
unequal pieces, one piece with only one element and one piece with the rest of the ele-
ments. It is ironic but true that our version of the quick-sort algorithm has its worst behav-
ior on an already sorted array. There are variations on the quick-sort algorithm that
perform well on a sorted array. For example, choosing the middle element as the splitting
value will give good performance on an already sorted array. But whatever splitting value
you choose, there will always be a few cases with this worst-case running time.
4. There is no unique answer, but below is one suitable answer:
+resize(double newRadius): void
+move(Pair<double, double> point): void
#erase( ): void
-radius: double
-center: Pair<double, double>
Circle
20_CH20.fm Page 852 Monday, August 18, 2003 2:08 PM
Programming Projects 853
5.
PROGRAMMING PROJECTS
1. Recode the quick-sort implementation using the modified pattern given in the program-
ming tip section entitled Pragmatics and Patterns.
2. Redo the Sorting pattern using classes. Define a template abstract class called
Sort that has
a constructor that takes an array argument. That array argument will be the array to be
sorted. The class
Sort will have member functions named the same and behaving the same
as the functions in Display 20.2, except that they will not have an array argument. The
array will be a member variable. Define the member function
sort following the model of
Display 20.2. The functions
split and join will be pure virtual functions (Chapter 15).
Then define derived classes called
Mergesort and Quicksort that realize the Sorting pat-
tern using the merge sort and quick-sort algorithms, respectively. The derived classes called
Mergesort and Quicksort have definitions for the member functions split and join.
Fully test all classes.
3. A vending machine accepts coins whose values sum to the price of a product or whose sum
exceeds the purchase price by one coin of any denomination. The vending machine then
accepts either a coin release button press that releases the inserted coins (for example, if the
customer decides not to buy) or a button press to release the product and any required
change, then resets the machine to a “start state.” The vending machine owner can remove
money, increase change in storage, or resupply the products on an as-needed basis.
Write a
design and code the corresponding program that models this situation.
4. In a two-story building a company has one elevator. We want to write a simulator for this
elevator so that the building designers can study the elevator system.
By each door on each floor there is a button to call the elevator. You decide what (if any)
displays should be provided for the user. There are buttons in the car for door open, door
close, and an alarm button. You will have to decide what outputs are needed from the sim-
ulator so that a user can see and understand what is happening in the simulation.
+constructors
+getLink( ): IntNode*
+getData( ): int
+setData(int theData): void
+setLink(IntNode* pointer); void
-data: int
-link: IntNode*
IntNode
20_CH20.fm Page 853 Monday, August 18, 2003 2:08 PM
854 Patterns and UML
Identify the main objects in this situation. Identify the behavior of each object. Decide how
the several objects interact. Then identify the classes and the member functions. Then iden-
tify the state that the class(es) must remember to be able to control the elevator.
We suggest a clock-driven simulation where everything happens on a clock tick (of say one
second, but your program will not have any timing. A click will just be one execution of a
loop.) You call a random-number generator to decide how many people arrive on each
floor at each tick.
Design and code a simulation of the building’s elevator.
This is not a hard exercise, but it requires clear thinking. It also requires that you supply
some of the details we have left out such as the bell ringing on arrival at a floor, and the
door opening, just to mention two things.
5. (Harder version of 4.) Generalize Programming Project 4 to simulate an elevator system
where the number of elevators is greater than one and the number of floors is greater than
two. In this version each floor will have two call buttons: one to call the elevator to go up
and one to call the elevator to go down. For definiteness, use 2 elevators and 10 floors, but
the principles will be the same regardless of the number. You have to decide on which of
the elevators is called when the call button is pressed under several situations. For example
suppose the elevator is called while one elevator is moving toward the caller’s floor while
the other is moving away. There are more than 4 such combinations, counting stopped
elevators.
You will have to decide what additional controls and what display to provide for the users
of the elevator. You will have to decide what outputs are needed from the simulator so that
a user can see and understand what is happening in the simulation.
20_CH20.fm Page 854 Monday, August 18, 2003 2:08 PM