Transcript Document

Lecture 4:
More UML Diagrams, Patterns
MISM
Summer 2001
Design Patterns




A "hot topic" in O-O research
Started by the book Design Patterns:
Elements of Reusable Object-Oriented
Software by Gamma, Helm, Johnson and
Vlissides (the Gang of Four).
The idea: some useful patterns of objects and
associations recur in lots of places. So, find
the "best" patterns, and encourage their use.
"Best'' usually means elegant, efficient,
reusable, flexible.
Design PAtterns


The idea of formalizing recurring patterns has
origins in architecture; see A Pattern
Language by Alexander, Ishikawa, Silverstein,
Jacobson, Fiksdahl-King and Angel.
“Each design pattern describes a problem
which occurs over and over again in our
environment, and then describes the core of
the solution to that problem, in such a way
that you can use this solution a million times
over, without ever doing it the same way
twice.”
Elements of a Pattern




Pattern name
Problem
Solution
Consequences
Example Pattern



Name: Adaptor
Intent: Convert the interface of a class into
another interface clients expect. Adaptor
lets classes work together that couldn't
otherwise because of incompatible interfaces.
Motivation: Sometimes a toolkit class that's
designed for reuse isn't reusable only
because its interface doesn't match the
domain-specific interface an application
requires.
Example 1



Suppose we have a linked list class:
Now we need a stack.
We can adapt the linked list class to a stack
by renaming the methods.
Example 1 (cont.)

This is the "class version'' of the
adaptor pattern:
Example 1 (cont.)
It's code looks like:
class linkedStack : private linkedList {
public:
linkedStack() {length = 0;}
void push(char c) {prepend(c); length++;}
char pop() {length--; return del(); }
int size() {return length;}
bool isEmpty() {return (length == 0);}
char top() {return first();}
void reset() {release(); length = 0;}
private:
int length;
};
Example 2





A drawing editor letting users draw and
arrange graphical elements (lines, polygons,
text, etc.) into pictures and diagrams.
There is a Shape class, and many subclasses.
The LineShape and CircleShape and so forth
are easy, but a TextShape is quite difficult.
There is a TextView class in the toolkit, but its
interface is quite different than that of Shape.
Solution: Define TextShape that adapts the
TextView interface to the Shape interface.
Example 2 (cont.)

Here is the “object version'' of this
pattern, for this example:
Consequences
The object adaptor




Lets a single Adaptor work with many Adaptees--that is, the Adaptee itself and all its subclasses
(if any).
The Adaptor can also add functionality to all
Adaptees at once.
Makes it harder to override Adaptee behavior. It
will require subclassing Adaptee and making
Adaptor refer to the subclass rather than the
Adaptee itself.
A Larger Example:
Designing a Document Editor
A WYSIWYG editor called Lexi, similar in
intent to the old MacWrite.
Design problems:





Document structure: what is the internal
representation? This affects everything else!
Formatting: how does Lexi arrange text and
graphics into lines and columns?
Embellishing the user interface: What if we want
to add scroll bars, borders, etc.? These might
change as the interface evolves.
Design Problems (cont.)




Supporting multiple look-and-feel standards: Motif,
Presentation Manager, Mac, Windows… We want
independence.
Supporting multiple window systems: MFC or TCL
or Borland or…
User operations: Buttons, dialogs, pull-down
menus, etc. Provide a uniform mechanism for
accessing this scattered functionality, and undoing
operations.
Spelling checking and hyphenation: How to
support a variety of analytical operations, without
having to make changes in lots of places?
Document Structure



One perspective: a document is just a
collection of basic graphical elements.
User perspective: the physical structure of
lines, columns, figures, tables, etc.
The user wants to manipulate this physical
structure directly, through Lexi's interface,
e.g., it should be possible to move an entire
table, not just the individual lines and
characters.
Internal Representation


Choose an internal representation that
matches the document's physical structure.
It should support



maintaining the physical structure (the
arrangement of lines into columns, etc.)
generating and presenting the document visually
mapping positions on the display to elements in
the internal representation.
Constraints



Text and graphics should be treated
uniformly, so one set of operations works for
both.
The internal representation shouldn't have to
distinguish between a single element and a
group of elements, so arbitrarily complex
structures can be built.
It should be possible to distinguish types of
elements; hyphenating a polygon makes no
sense.
Recursive composition
Text and Graphics
Object Structure
Glyphs

Glyphs have three basic responsibilities.
They know




how to draw themselves,
what space they occupy, and
their children and parent.
This example shows the Composite
pattern.
Glyph Operations
Responsibility
Operations
appearance
virtual void Draw(Window)
virtual void Bounds(Rect)
hit detection
virtual bool Intersects(const Point)
structure
virtual
virtual
virtual
virtual
void Insert(Glyph, int)
void Remove(Glyph)
Glyph Child(int)
Glyph Parent()
Formatting

The Glyph arrangement gives a
representation of the document's physical
structure. But how to determine that
structure?




Determining the structure is Lexi's job; the user
expects line breaking to happen for free.
How will a formatting algorithm be applied to the
data structure?
Formatting algorithms come in many flavors, but
they are mostly all complex. How to keep multiple
algorithms encapsulated, and separate from the
document structure?
Can we change the algorithm at run-time?
Strategy Pattern


A solution is to provide a Compositor
class, having subclasses for the various
formatting algorithms.
Then, a new Glyph subclass called
Composition is added.
Composition/Compositor
Composition/Compositor

A Composition object gets an instance of a
Compositor subclass when it's created, and it
tells the compositor to Compose its glyphs
when something changes (user adds new
glyphs, reformats, etc.).
Responsibility
Operations
what to format
void
SetComposition(Composition)
when to format
virtual void Compose()
Unformatted Composition


An unformatted Composition contains
only the visible glyphs (the document's
basic content, no line breaks).
This probably should never be visible!
Composition/Compositor
Embellishing the User
Interface


Suppose we want to be able to add a border,
and scroll bars (and maybe more).
One way: create subclasses of the
Composition class. How many might we end
up with?



BorderedComposition,
ScrollableComposition,
BorderedScrollableComposition, etc.
Embellishments (cont.)



Maybe object composition is a better
way. Suppose we make another class
called Border.
But does Glyph contain Border, or does
Border contain Glyph?
The latter requires no change in Glyph.
Embellishments (cont.)




Borders have appearance, so they are a kind
of Glyph.
Also, clients shouldn't care whether glyphs
have borders or not.
When clients tell a plain, unbordered glyph to
draw itself, it should do so without
embellishment.
If that glyph is in a border, the client
shouldn't have to do anything differently.
Embellishments (cont.)



So, make Border a subclass of Glyph.
This is the idea of a transparent enclosure.
This combines single-child composition and
compatible interfaces. Clients can't tell if
they're dealing with the component or its
enclosure.
Declare a subclass of Glyph called
MonoGlyph as an abstract class for
``embellishment glyphs.''
The Decorator Pattern
Transparency
MonoGlyph stores a reference to a component and forwards all requests to it. So by
default MonoGlyph is transparent to its user.
void MonoGlyph::Draw(Window w) {
_component.Draw(w);
}
At least one operation of the subclass extends the forwarded behavior:
void Border::Draw(Window w) {
MonoGlyph::Draw(w);
DrawBorder(w);
}
Multiple Embellishments
To get just a border around a composition, create the composition, then ``wrap it'' in a
Border, and then draw it:
Window w = new Window;
Composition comp = new Composition;
Border bordComp = new Border(comp);
bordComp.Draw(w);
To make a bordered, scrollable composition, just repeat the pattern:
Window w = new Window;
Composition comp = new Composition;
Border bordComp = new Border(comp);
Scroller scrBordComp = new Scroller(bordComp);
scrBordComp.Draw(w);
Supporting Multiple Look-andFeel Standards





Can Lexi be easily retargeted to a different interface
standard? Think Motif, X, Windows, Presentation
Manager, and so on.
Suppose we have GIU libraries for each standard.
Suppose they all support the same “widgets,” that is,
dialogs, buttons, scrollbars and so forth.
We don't want hard-coded instances like
ScrollBar sb = new MotifScrollBar;
We might forget to change one, and end up with a
Motif menu a Mac application!
Try This
scrollBar sb = new
guiFactory.createScrollBar;
where guiFactory is an instance of a
MotifFactory class. CreateScrollBar returns
a new instance of the proper ScrollBar
subclass for the look and feel desired.
Abstract Factory Pattern


For clients, the effect is the same as asking
for a MotifScrollBar, but Motif isn't mentioned
by name.
How to make this work?


We'll use an abstract class GUIFactory, that has
methods like CreateScrollBar and CreateButton.
Subclasses implement these operations to return
glyphs such as MotifScrollBar or PMScrollBar.
Abstract Factory Pattern
Getting That Look

If it's clear at compile time what the
look and feel will be, then write
GUIFactory guiFactory = new
MotifFactory;
Or Do It At Startup
GUIFactory guiFactory;
const char styleName =
getenv("LOOK_AND_FEEL");
if (strcmp(styleName,"Motif") == 0) {
guiFactory = new MotifFactory;
}
else if (strcmp(styleName,
"Presentation_Manager") == 0) {
guiFactory = new PMFactory;
}
User Operations


Some of Lexi's functionality is through the
WYSIWYG representation (enter/delete text,
select ranges, etc.).
Other functionality is accessed through
menus, buttons and keyboard accelerators.
Things like






create a new document
open, save, print a document
cutting and pasting
changing fonts and styles
changing formatting (alignment, justification)
quitting
User Operations (cont.)




There may be multiple ways of accessing
these features, for instance from the menu,
or by using an accelerator, or clicking a
toolbar tool.
These operations are implemented in many
different classes (so let's try to reduce
dependencies). We also want undo and redo.
The solution is to encapsulate requests.
A pull-down menu is just another kind of
glyph, but one that does something in
response to a mouse-up event.
The Solution




Make MenuItem a subclass of Glyph, with the ability
to do some work in response to a request from a
client (probably an event dispatcher).
One approach: make a subclass of MenuItem for
each operation. But we don't make a glyph subclass
for every letter, so this seems wrong.
Besides, we don't want to repeat the code in multiple
places (for a menu item, a accelerator, a tool).
So, encapsulate the command, then attach it to the
menu item.
Command Pattern
MenuItem-Command
Relationship
Buttons and other widgets can use commands in the same way.
Undoability




Some commands should be reversible.
The command for changing a font could
store the range of text and the old font.
Some commands should be reversible,
but others not.
So add a Reversible() method to
Command that returns a Boolean.

Lexi can keep track of a command history,
perhaps a doubly-linked list, like this:
past commands
present

Each circle is a command object. Then undo
and redo simply march along the command
history.
Spell Check and Hyphenation




Once again several algorithms are available,
with time/space/quality tradeoffs.
There are other operations that we’d like to
apply to the whole document, or to part
(searching, word counting, complexity
analysis, etc.).
But the Glyph class shouldn’t have to change
when a new capability is added.
Two problems: Accessing the information,
and doing the analysis.
Accessing the Information




In accessing information, different
Glyphs may store their children in
different data structures.
Also, we may need to search in reverse
order.
Currently, a Glyph uses an integer to
locate its children.
We can improve this:
Accessing the Information
void First(Traversal kind)
void Next()
bool IsDone()
Glyph GetCurrent()
void Insert(Glyph)

The type Traversal is an enumeration, with
values such as PREORDER and REVERSE.
Drawbacks



This approach isn’t very good, because it’s
difficult to add new traversal strategies.
Putting this code in the Glyph classes would
force us to constantly rewrite Glyph.
Once again, encapsulate the concept that
varies, this time by introducing iterators.
Iterator Pattern
Iterator
*
First( )
Next( )
IsDone( )
CurrentItem( )
iterators
PreorderIterator
Array Iterator
currentItem
First( )
Next( )
IsDone( )
CurrentItem( )
First( )
Next( )
IsDone( )
CurrentItem( )
root
Gly ph
...( )
CreateIterator( *
Iterators



We can add new types of traversals
without modifying the glyph classes.
Iterators store their own copy of the
state of traversal, we can have multiple
traversals going simultaneously.
We could also parameterize iterators, so
they would apply to other classes as
well.
Traversals vs. Traversal
Actions



Where should the responsibility for actions
be? In the iterator itself? No!
Different analyses require the same type of
iterator. We could put specific functions like
CheckMe(SpellingChecker checker)
in each glyph, but “that would be wrong.”
Better to use the Visitor pattern.
Visitor Pattern
Visitor
VisitCharacter (Character)
VisitRow (Row)
VisitImage (Image)
Hy phenationVisitor
SpellCheckVisitor
Gly ph
... ()
Accept(Visitor)
Part II
Object-Interaction Diagrams



Class diagrams represent the static
structure of a O-O system.
The dynamic (runtime) structure must
be represented differently.
We use object-interaction diagrams


Collaboration diagrams
Sequence diagrams
Object-Interaction Diagrams



Typically, interaction diagrams are
written for each use case.
If the use case is complicated, or has
many alternate courses, several
diagrams can be drawn.
The idea is to show message passing
arrangements between objects.
A Class Diagram

Some classes:
JApplet
contentPane : JFrame
getContentPane( )
init( )
MyApplet
JFrame
add(
JLabel
public class MyApplet extends JApplet {
public void init() {
getContentPane().add(new JLabel("Applet!);
}
}
Collaboration Diagram

Boxes are objects, arrows are messages.
getContentPane ( )
add (new JLabel("Applet!"))
init ( )
applet : MyApplet
main

myContentPane : JFrame
Lines joining objects are links; they should
exist (as associations) in the class diagram.
Those Little Numbers

Indicate the sequence of messages
2: getContentPane ( )
1: init ( )
main


3: add ( new JLabel("Applet!()))
applet : MyApplet
myContentPane : JFrame
These can be a pain; it took me three tries to
get this right!
But automatic tool conversion to sequence
diagrams requires them…
Another Example
OrderTaker
request ( : Order, : Customer) :
CreditBureau
checkCredit ( : Customer) : Currency
debit ( : Customer, : Currency)
TicketDB
reserve (anOrder : Order) : Currency
And the Collaboration Diagram
requestor (supposed to be an actor)
1: request (Order, Customer)
3: reserve (Order)
: OrderTaker
2: checkCredit (Customer)
4: debit (Customer, Currency)
: CreditBureau
: TicketDB
Polymorphism

What to do if we don’t know the exact
target of a message?
Instrument
Conductor
tune( )
Wind
Percussion
Stringed
Instrument[] orchestra = new Instrument[5];
tuneAll(orchestra);
Polymorphism (cont.)

Send the message to the highest class
containing all possible objects.
tune ( )
ozawa : Conductor

outOfTune : Instrument
Page-Jones recommends putting the
class name in parentheses.
Iterated Messages
Instrument
Orchestra
tune( )
Wind
1..*
Percussion
Stringed
tune ( )
bostonSymphany : Orchestra


: Instrument
Probably should be able to add aggregation
diamond to the collaboration diagram.
If you’re using an iterator, you may or may
not show calls to first() and last().
Self-Messages

No need to refer to “self” (“this”) here:
2: getContentPane ( )
1: init ( )
main

3: add ( new JLabel("Applet!()))
applet : MyApplet
myContentPane : JFrame
However, if you do need to pass a
handle in a message, show it explicitly.
Sequence Diagrams



Just a different format for “collaboration
diagrams with numbers.”
The temporal sequence is much clearer.
Time “moves” downward.
Class Diagram
BankAccount
Customer
ow ner( )
w ithdraw Funds(
)
standing( )
permittedMinBalance( )
Transfer
TM
new ( )
begin( )
rollback(
)
DESCRIPTION of :Transfer.makeTransfer : Boolean
create a new transfer transaction
begin transaction
establish fromAccount
establish toAccount
if the two owners are the same customer, who also
has good customer standing,
then fromAccount.withdrawFunds(amt, out withdrawal OK)
else transferXaction.rollback()
endif
if withdrawalOK
then toAccount.depositFunds(amt, out depositOK)
else transferXaction.rollback()
endif
if depositOK
then transferXaction.commit()
return true
else transferXaction.rollback()
return false
endif
: Transfer
fromAccount : toAccount : accountOwner
transfer
BankAccount BankAccount : Customer
Xaction : TM
new ( )
begin ( )
owner ( )
owner ( )
standing ( )
rollback ( )
withdrawFunds ( )
permittedMinBalance ( )
depositFunds ( )
rollback ( )
commit ( )
rollback ( )
State Diagrams

These are useful for objects or
attributes that have



a small number of states or values, and
restrictions on permitted transitions.
Not too many objects/attributes satisfy
these conditions…
Pittsburgh Intersection
state
Intersection
directio
n
Road
time-out[ cars in N/S left lane ]
North/south may go straight
North/south may go left
time-out[ no cars in E/W left lane ]
time-out
event
time-out
time-out[ no cars in N/S left lane ]
East/west may turn left
East/west may go straight
time-out[ cars in E/W left lane ]
guard
The Ticket Machine
initial state
normal transition
abnormal transition
final state
Purchasing
identify
on fail: exit
insert card
/ reset selection
TicketMachine
ins ertCard ()
identify (PIN : int) : boolean
pick (aSeat : Seat)
resum e ()
buy ()
confirm ()
s ell ()
cancel ()
completed transaction
selecting
on pick( seat ): add to selection(seat)
idle
push resume
push buy
confirming
push cancel
outer transition
aborts internal
activity
push confirm
selling
entry: sell()
internal transition