Download as ppt, pdf, or txt
Download as ppt, pdf, or txt
You are on page 1of 63

Basic Design Patterns

2. Designing a Document Editor


Gabriel Maana Ed. 453 Of. 120 Ext. 14080 gjmananag @ unal.edu.co

Basic Design Patterns


Document Structure

Formatting
User Interface

Supporting Look & Feel Standards


Supporting Window Systems

User Operations
Spelling Checking & Hyphenation

Basic Design Patterns


Document Structure (Composite)
The choice of internal representation for the document affects nearly every aspect of the editors design. All editing, formatting, displaying and textual analysis will require traversing the representation. The way we organize this information will impact the design of the rest of the application.

Document Structure
text line chars table image ...

paragraph

column

page

Document Structure
The internal representation should support:
Maintaining the documents physical structure, that is, the arrangement of text and graphics into lines, columns, tables, etc.
Generating and presenting the document visually Mapping positions on the display to elements in the internal representation

Document Structure
Constraints:

It should treat text and graphics uniformly


The implementation shouldnt have to distinguish between single elements and groups of elements in the internal representation

classes must have compatible interfaces

Glyph

draw(Window) intersects(Point) insert(Glyph, int)

children

Character draw(Window w) intersects(Point p) char c

Rectangle draw(...) intersects(...)

Row draw(Window w) intersects(Point p) insert(Glyph, int)

Polygon
return true if p intersects this character

draw(...) intersects(...)

insert g into children at position i

w->drawChar(c) for all c in children if c->intersects(p) return true

Recursive Composition

for all c in children ensure c position c->draw(w)

Composite Pattern
Recursive composition is good for more than just documents. It can be used to represent any potentially complex, hierarchical structure. The Composite pattern captures the essence of recursive composition in object-oriented terms.

Basic Design Patterns


Formatting (Strategy)
How does the editor arrange text and graphics into lines and columns?

What objects are responsible for carrying out different formatting policies?
How do these policies interact with the documents internal representation?

Basic Design Patterns


Formatting
Responsibility what to format when to format Operation void setComposition( Composition ) void compose()

well define a Compositor class for objects that can encapsulate a formatting algorithm the glyphs it formats are the children of a special Glyph subclass called Composition

Glyph insert(Glyph, int)


children

Strategy

Composition insert(Glyph, int)

compositor

Compositor compose() setComposition()

composition

Glyph::insert(g, i) compositor.compose()

SimpleCompositor compose()

ArrayCompositor compose()

TeXCompositor compose()

Strategy Pattern
Encapsulating an algorithm in an object is the intent of the Strategy pattern. The key participants in the pattern are strategy objects and the context in which they operate. Compositors are strategies; they encapsulate different formatting algorithms. A Composition is the context for a Compositor strategy.

Basic Design Patterns


User Interface (Decorator)
The editors UI includes scroll-bars, borders and drop shadows.

Such embellishments are likely to change as the editors UI evolves.


Hence it is important to be able to add and remove embellishments easily without affecting the rest of the application.

Basic Design Patterns


User Interface
Embellishing the users interface involves extending existing code.

Using inheritance to do such extension precludes rearranging embellishments at runtime. An equally serious problem, is the explosion of classes that can result from an inheritancebased approach.

Composition

BorderedCompsosition

ScrollableComposition

Composition

BorderedCompsosition

ScrollableComposition

BorderedScrollableComposition

User Interface
Object composition offers a potentially more workable and flexible extension mechanism:
Since we know were embellishing an existing glyph, we could make the embellishment itself an object (class Border f.i.)

Glyph and Border: the next step is to decide who composes whom
If we put the Border in the Glyph then we must make modifications to all Glyph subclasses to make them aware of the border

User Interface
All this lead us to the concept of transparent enclosure, which combines the notions of (1) single child (or single-component) composition, and (2) compatible interfaces
Clients generally cant tell whether theyre dealing with the component or its enclosure: the enclosure delegates all its operations to its component and augments the components behaviour by doing work of its own (before or after) delegating an operation.

Monoglyph
Glyph draw(Window)

MonoGlyph draw(Window)

component

Border draw(Window) drawBorder(Window)

Scroller draw(Window)

Decorator Pattern
The Decorator pattern captures class and object relationships that support embellishment by transparent enclosure. In the Decorator pattern, embellishment referes to anything that adds responsibilities to an object.

Basic Design Patterns


Supporting multiples L&F Standards (Abstract Factory)
The editor should adapt easily to different look-and-feel standards such as Motif and Windows GUI without major modification.

GUIFactory createButton() createMenu() ...

Factory Hierarchy

MotifFactory createButton() createMenu() ...

WindowsFactory createButton() createMenu() ...

MacFactory createButton() createMenu() ...

return new MotifMenu return new MotifButton

return new WindowMenu return new WindowButton

return new MacMenu return new MacButton

Glyph ...

Product Hierarchy
Menu popup() ...

Button pressed() ...

MotifButton pressed() ...

MacButton pressed() ...

MotifMenu popup() ...

MacMenu popup() ...

WindowsButton

WindowsMenu

pressed() ...

popup() ...

Abstract Factory Pattern


Factories and Products are the key participants in the Abstract Factory pattern. This pattern captures how to create families of related product objects without instantiating classes directly. Its most appropriate when the number and general kinds of products objects stay constant, and there are differences in specific product families.

Abstract Factory Pattern


The Abstract Factory patterns emphasis on families of products distinguishes it from other creational patterns, which involve only one kind of product object.

Basic Design Patterns


Supporting multiples Window Systems (Bridge)
Different look-and-feel standards are usually implemented on different window systems. The editors design should be as independent of the window system as possible.

Basic Design Patterns


Can we use an Abstract Factory?
At first glance this may look like another opportunity to apply the Abstract Factory pattern. But the constraints for window system portability differ significantly from those for look-and-feel independence.

Basic Design Patterns


Answer: NO
In applying the Abstract Factory pattern, we assumed that we would define the concrete widget glyphs classes for each L&F standard. Its highly unlikely that window class hierarchies from different vendors are compatible in any way.
Hence, we wont have a common abstract product for each kind of widget (ScrollBar, Button, etc.)

Basic Design Patterns


We have to make the different widget hierarchies adhere to a common set of abstract product interfaces. Only then could we declare the create...() operations properly in our abstract factorys interface. We need a uniform set of windowing abstractions that let us take different window system implementations and slide any one of them under a common interface.

Basic Design Patterns


Encapsulating Implementation Dependencies
The Window class encapsulates the things that windows tend to do across window systems:

Provide operations for drawing basic geometric shapes


Iconify and de-iconify themselves Resize themselves (Re)draw their contents on demand

Basic Design Patterns


Two extreme philosophies:
The Window class must span the functionality of windows from different window systems:
1. Intersection of functionality The window class provides only functionality thats common to all window systems 2. Union of functionality Create an interface that incorporates the capabilities of all window systems

Basic Design Patterns


Window class interface
Responsibility window management Operations virtual virtual virtual virtual virtual ... void void void void void redraw() raise() lower() iconify() deiconify()

graphics

virtual void drawLine() virtual void drawRect() virtual void drawText() ...

Glyph draw( Window )

glyph

Window redraw() iconify() ... drawLine() drawText() ...


glyph->draw( this )

FrameWindow

IconWindow iconify()

DialogWindow lower()
owner

owner->lower()

Basic Design Patterns


Now we have defined a window interface for the document editor to work with, where does the real platform-specific window come in? We can do the same thing we did for formatting and embellishment, namely, encapuslate the concept that varies.

Window raise() ... drawText() ...


imp

WindowImp deviceRaise() ... deviceText() ...

FrameWindow

DialogWindow

IconWindow MacWindowImp deviceRaise() MSWindowImp deviceRaise()

XWindowImp deviceRaise()

Bridge Pattern
The intent behind Bridge is to allow separate class hierarchies to work together even as they evolve independently.
The Bridge pattern lets us maintain and enhance our logical window abstractions without touching window systemdependent code, and vice versa.

Basic Design Patterns


User Operations (Command)
Users control the editor through various user interfaces, including buttons and pull-down menus. The functionality behind these interfaces is scattered throughout the objects in the application. The challenge here is to provide a uniform mechanism both for accessing this scattered functionality and for undoing its effects.

Command execute()

Command Hierarchy

PasteCommand execute() --------------m_buffer

FontCommand execute() --------------m_font

SaveCommand execute()

paste m_buffer into document

make selected text appear in m_font

pop up a dialog box that lets the user name the document and then save the document

Glyph

MenuItemCommand

MenuItem clicked()

command

Command

execute()

command->execute()

Command Pattern
The Command pattern describes how to encapsulate a request.
It prescribes a uniform interface for issuing requests that lets you configure clients to handle different requests. The interface shields clients from the requests implementation.

Basic Design Patterns


Spelling Checking and Hyphenation (Iterator/Visitor)
How does the editor support analytical operations such as checking for misspelled words and determining hyphenation points? How can we minimize the number of classes we have to modify to add a new analytical operation?

Basic Design Patterns


Spelling Checking and Hyphenation
As was the case with line-breaking strategies, theres more than one way to check spelling and compute hyphenation points. So here too we want to support (and add easily) multiple algorithms.

We also want to avoid wiring this functionality into the document structure.

Basic Design Patterns


Spelling Checking and Hyphenation
There are actually two pieces to this puzzle: 1. accessing the information to be analyzed, which we have scattered over the glyphs in the document structure, and

2. doing the analysis.

Basic Design Patterns


Spelling Checking and Hyphenation
The text we need to analyse is scattered throughout a hierarchical structure of glyphs objects. Some glyphs might store their children in linked lists, others might use arrays, and still others might use more esoteric data structures.
The access mechanism must be able to handle all of these possibilities.

Basic Design Patterns


Encapsulating Access and Traversal
An important role of the glyphs abstraction is to hide the data structure in which children are stored. That way we can change the data structure a glyph class uses without affecting other classes. We can solve this problem and support several different kinds of traversals at the same time.

Basic Design Patterns


Encapsulating Access and Traversal
We can add the following abstract operations to the Glyphs interface:

void first( Traversal ) void next() boolean isDone() Glyph current() void insert( Glyph )

iterators

1..*

Iterator first() next() isDone() current()

glyph->draw( this )

PreorderIterator first() next() isDone() current()

ArrayIterator first() next() isDone() current() currentItem


1..*

ListIterator first() next() isDone() current()

NullIterator first() next() isDone() current()

1..*

return true

root

Glyph
... createIterator()
return new NullIterator

Iterator Pattern
The Iterator pattern captures the techniques for supporting access and traversal over object structures.
Its applicable not only to composite structures but to collections as well.

Iterator Pattern
The Iterator pattern abstracts the traversal algorithm and shields clients from the internal structure of the objects (containers) they traverse.
It illustrates once more how encapsulating the concept that varies helps us gain flexibility and reusability.

Basic Design Patterns


Traversal vs. Traversal Actions
Now that we have a way of traversing the glyph structure we can check the spelling and do the hyphenation. Both analyses involves accumulating information during the traversal.
First we have to decide where to put the responsibility for analysis (1. iterator classes, 2. glyph classes, 3. separate class).

Basic Design Patterns


Traversal vs. Traversal Actions
1. Different analyses might require the same kind of traversal: analysis and traversal should be separate. 2. Well have to change every glyph class whenever we add a new kind of analysis.
3. We need to encapsulate the analysis in a separate object: the iterator would carry the instance to each glyph in the structure.

Basic Design Patterns


Traversal vs. Traversal Actions
The fundamental question with this approach is how the analysis object distinguishes different kinds of glyphs without resorting to type tests or downcasts:

Basic Design Patterns


Traversal vs. Traversal Actions
void SpellingChecker::check( Glyph g ) { Char c; Row r; Image i;
if (c = dynamic_cast<Char>( g )) { ... // else, else

Basic Design Patterns


Traversal vs. Traversal Actions
void Glyph::checkMe( SpellingChecker )

Basic Design Patterns


Traversal vs. Traversal Actions
void GlyphSubClass::checkMe( SpellingChecker checker ) { checker.checkGlyphSubClass( this ); }

Basic Design Patterns


Traversal vs. Traversal Actions
Using overloading: void GlyphSubClass::checkMe( SpellingChecker checker ) { checker.check( this );

Basic Design Patterns


Traversal vs. Traversal Actions
This approach works for finding spelling errors, but how does it help us support multiple kinds of analyses? It looks like we have to add an operation like checkMe( SpellingChecker ) to Glyph and its subclasses whenever we add a new kind of analysis.

Basic Design Patterns


Traversal vs. Traversal Actions
Well use the term visitor to refer generally to classes of objects that visit other objects during a traversal and do something appropriate.

Basic Design Patterns


Traversal vs. Traversal Actions
abstract class Visitor { public void visit( Char ); public void visit( Row ); public void visit( Image ); ...

}
(polymorphism)

Basic Design Patterns


Traversal vs. Traversal Actions
void Glyph::visitMe( Visitor v ) { v.visit( this ); }

Now adding a new analysis requires just defining a new subclass of Visitor: we dont have to touch any of the Glyph classes.

Visitor Pattern
The Visitor class and its subclasses described are the key particpants in the pattern.
Visitor lets you define a new operation without changing the classes of the elements on which it operates.

Summary
1. Composite to represent the documents physical structure 2. Strategy to allow different formatting algorithms

3. Decorator for embellishing the user interface


4. Abstract Factory for supporting multiple look & feel standards

Summary
5. Bridge to allow multiple windowing platforms 6. Command for undoable user operations 7. Iterator for accessing and traversing object structures 8. Visitor for allowing an open-ended number of analytical capabilities without complicating the documents structure implementation

You might also like