60
Introducing Design Patterns Through a Case Study Designing A Document Editor Daniel POP, Ph.D

Introducing Design Patterns Through a Case Study Designing A Document Editor

  • Upload
    kuri

  • View
    40

  • Download
    1

Embed Size (px)

DESCRIPTION

Introducing Design Patterns Through a Case Study Designing A Document Editor. Daniel POP, Ph.D. The Plan Design Patterns: Elements of Reusable Object-Oriented Design. Review the Case Study in the book 7 Design Problems Discuss each design problem Review the solutions. - PowerPoint PPT Presentation

Citation preview

Page 1: Introducing Design Patterns Through a Case Study Designing A Document Editor

Introducing Design Patterns Through a Case Study

Designing A Document Editor

Daniel POP, Ph.D

Page 2: Introducing Design Patterns Through a Case Study Designing A Document Editor

2

The PlanDesign Patterns: Elements of Reusable Object-Oriented Design

• Review the Case Study in the book

• 7 Design Problems• Discuss each design

problem• Review the solutions

Page 3: Introducing Design Patterns Through a Case Study Designing A Document Editor

3

Lexi Case StudyFeatures• LEXI is a WYSIWYG document editor with the following features:• Mix text and graphics in a variety of styles• Pull-down menus • Scrollbars• Icons for jumping to a particular page

Page 4: Introducing Design Patterns Through a Case Study Designing A Document Editor

4

Seven Design Problems

1. Document Structure How do we represent a document?

2. Formatting How do we arrange text and graphics on the screen (or

paper)

3. Embellishing the user interface

4. Supporting multiple look-and-feel standards

5. Supporting multiple window systems

6. User operations

7. Spelling checking and hyphenation

Page 5: Introducing Design Patterns Through a Case Study Designing A Document Editor

5

Design Issue #1:Document Structure

• Affects nearly every aspect of Lexi’s design• What are the impacts of the structure we choose?• What do we need to consider?

Page 6: Introducing Design Patterns Through a Case Study Designing A Document Editor

6

Design Issue #1: Document Structure

•Documents are a combination of characters, lines, polygons, images etc.•Often a user will want to deal with things at a higher level (ex. a picture or a row or column of a document)•To make Lexi user-friendly, we need to allow the user to deal with these higher level constructs•The internal representation of the document structure should match the physical structure• Allow arrangement of text and graphics into lines, columns, tables, etc.• Need to draw the document on the screen

Page 7: Introducing Design Patterns Through a Case Study Designing A Document Editor

7

Design Issue #1: Document Structure

Page 8: Introducing Design Patterns Through a Case Study Designing A Document Editor

8

Design Issue #1: Document Structure

Recursive Composition• A method for representing a hierarchy of information• A grouping of simple items to create a composite item• Groupings of items can be a part of even higher level groups

Page 9: Introducing Design Patterns Through a Case Study Designing A Document Editor

9

Design Issue #1: Document Structure

• Therefore…– Objects need corresponding classes– All of these classes need compatible interfaces so that

allows us to handle them uniformly• Meet the Glyph

Responsibility Operations

Appearance virtual void Draw(Window*)

virtual void Bounds(Rect&)

Hit Detection virtual bool Intersects(const Point&)

Structure virtual boid Insert(Glyph*, int)

virtual void Remove(Glyph*)

virtual Glyph* Child(int)

virutal Glyph* Parent()

Page 10: Introducing Design Patterns Through a Case Study Designing A Document Editor

10

Design Issue #1: Document Structure

Page 11: Introducing Design Patterns Through a Case Study Designing A Document Editor

11

Design Issue #1: Document Structure

• Recursive Composition – It’s not just for documents• Useful for any potentially complex, hierarchical

structure

• The Composite pattern captures this design approach

Intent: Compose objects into tree structures to represent part-whole hierarchies.

Composite lets clients treat individual objects and compositions of objects uniformly.

Page 12: Introducing Design Patterns Through a Case Study Designing A Document Editor

12

Design Issue #1: Document StructureThe Composite Pattern

Defines behavior

For the primitivesForwards requests to children

Behavior for composites

Stores Child components

Page 13: Introducing Design Patterns Through a Case Study Designing A Document Editor

13

Design Issue #1: Document Structure

• How ‘deep’ do we go? – Ideally, use objects to represent finer details

(characters) to promote flexibility at finest levels of the application

• How to represent each character (its position, style etc) in an efficient way?

Objects are often not used because of high costs in terms of memory allocation.

Page 14: Introducing Design Patterns Through a Case Study Designing A Document Editor

14

Design Issue #1: Document Structure

Sharing objects

Create a pool of objects that are (re)used in more than one place

Page 15: Introducing Design Patterns Through a Case Study Designing A Document Editor

15

Design Issue #1: Document Structure• Meet the Flyweight pattern

• that is a shared object that can be used in multiple contexts simultaneously

• Contexts is captured in the extrinsic state; flyweight stores only the intrinsic state

• Flyweight: character objects• Intrinsic state: character code, • Extrinsic state: coordinate position in the document, its typographic style

etc (these can be determined from the text layout algorithms and formatting commands in effect wherever the character appears)

Page 16: Introducing Design Patterns Through a Case Study Designing A Document Editor

16

Design Issue #1: Document Structure

• The Flyweight pattern• Intent: Use sharing to support large numbers of fine-

grained objects efficiently.

• Flyweight seeks to reduce intrinsic state of an object and externalize details in an extrinsic state.

• For LEXI, because the number of different character objects is far less than the number of characters in the document, the total number of objects is substantially less than what a naive implementation would use.

Page 17: Introducing Design Patterns Through a Case Study Designing A Document Editor

17

Design Issue #1: Document StructureThe Flyweight Pattern

Reduced intrinsic state with each object

Additional data necessary to perform an operation (extrinsic state) is sent as parameters

Page 18: Introducing Design Patterns Through a Case Study Designing A Document Editor

18

Design Issue #1: Document Structure

• Context for LEXI document structure can be stored as a map of typographic information, using B-tree data structure (http://en.wikipedia.org/wiki/B-tree), in a separate structure rather than store the font and type style with each character object.

• The map keeps track of runs of characters with the same typographic attributes. When a character draws itself, it receives its typographic attributes as a side-effect of the draw traversal.

• Because documents normally use just a few different fonts and styles, storing this information externally to each character object is far more efficient than storing it internally.

Page 19: Introducing Design Patterns Through a Case Study Designing A Document Editor

19

Design Issue #1: Document Structure

• How to efficiently handle large objects (for example images) within the document structure?– The Proxy pattern addresses this issue.

Object Diagram

Pattern structure

Page 20: Introducing Design Patterns Through a Case Study Designing A Document Editor

20

Design Issue #1: Document Structure

• To conclude the Design Issue #1: Several patterns– Composite

– Flyweight

– Proxy

…and advanced data structure– B-tree

were combined to achieve a flexible and effective representation of a complex document.

Page 21: Introducing Design Patterns Through a Case Study Designing A Document Editor

21

Design Issue #2:Formatting

• How to construct a particular physical structure • And maintain separation of data and format

• Properly formatted document• Some possible responsibilities:

• Break text into lines

• Break lines into columns

• Margin Widths

• Indentation

• Tabulation

• Single/Double Spacing

• Authors restrict the LEXI example to breaking glyphs into lines

Page 22: Introducing Design Patterns Through a Case Study Designing A Document Editor

22

Design Issue #2:Formatting

• How should we approach formatting?

• What are some important trade-offs?– Formatting quality vs. formatting speed– Formatting speed vs. Storage requirements

• What are the design goals?– Keep it well-contained

• Don’t spread it over many classes

– Independent of document structure• Add a new glyph… not have to worry about changing

format code• Add new formatting algorithm – not have to change glyphs

Page 23: Introducing Design Patterns Through a Case Study Designing A Document Editor

23

Design Issue #2Formatting

• It will be complex… for sure• Ideas… ideas… ideas…

Page 24: Introducing Design Patterns Through a Case Study Designing A Document Editor

24

Design Issue #2Formatting

• Needs to be easy to change the formatting algorithm– If not at run-time, at least at compile-time

• We can make it independent, self contained and replaceable by putting it in its own class

• We can make it run-time replaceable by creating a class hierarchy for formatting algorithms

• Compositor

Responsibility Operations

What to format void SetComposition(Composition)

When to format

virtual void Compose()

Page 25: Introducing Design Patterns Through a Case Study Designing A Document Editor

25

Design Issue #2Formatting

• Composition object – when created contains the glyphs that determine content, but not structure (such as row, column)

• When Compose() is called, it iterates the glyphs and composes (formats) them.

Page 26: Introducing Design Patterns Through a Case Study Designing A Document Editor

26

Design Issue #2Formatting

• Rows and Columns are inserted by the compositor• Why rows and columns?

– Inserted by the line-breaking algorithm

Page 27: Introducing Design Patterns Through a Case Study Designing A Document Editor

27

Design Issue #2Formatting

• Why do we need different Compositor’s?– In the Example:

• SimpleCompositor might do a quick pass without regard for such esoterica as the document's "color." Good color means having an even distribution of text and whitespace.

• A TeXCompositor would implement the full TeX algorithm, which takes things like color into account in exchange for longer formatting times.

• Compositor-Composition class split ensures a strong separation between code that supports the document's physical structure and the code for different formatting algorithms

• We can change the line-breaking algorithm at run-time by adding a single SetCompositor operation to Composition's basic glyph interface.

Page 28: Introducing Design Patterns Through a Case Study Designing A Document Editor

28

Design Issue #2Formatting

• Meet the Strategy pattern– Intent: Encapsulating an algorithm in an object– Key participants in the pattern are

• Strategy objects (Compositors)• Context object (Composition)

– The key to using Strategy• Interfaces for the strategy and the context that will

support a range of algorithms• Ideally we don’t want to change these interfaces to

support a new algorithm

Page 29: Introducing Design Patterns Through a Case Study Designing A Document Editor

29

Design Issue #2Formatting

• A reflection before moving forward…– Encapsulating an algorithm in an object is the intent of

the Strategy pattern.

– Distributing an algorithm among several objects is the intent of the State pattern• Actually not used in this case-study• Related to Strategy though, sharing similar diagrams, but

completely different semantic

Page 30: Introducing Design Patterns Through a Case Study Designing A Document Editor

30

Design Issue #3Embellishing the user interface

• Two ornaments (embellishments):– a border around the text editing area – scroll bars

Page 31: Introducing Design Patterns Through a Case Study Designing A Document Editor

31

Design Issue #3Embellishing the User Interface

• Basically, we want to extend the code to provide a Transparent Enclosure– Transparent in that the page itself does not know

anything about the changes – it behaves the same

• How should we do this?– We could use Inheritance, how would that look?– We have a Composition class…

• To add a Border we add a BorderedComposition class• To add a Scroll bar we add a ScrollableComposition class• What about both? BorderedScrollableComposition class?

• How could we do it with object composition instead?– What object “has” what object? – How do we make it extensible?

Page 32: Introducing Design Patterns Through a Case Study Designing A Document Editor

32

Design Issue #3Embellishing the User Interface

• Meet the Decorator pattern// Delegate it

void MonoGlyph::Draw (Window* w)

{

_component->Draw(w);

}

// Do it

void Border::Draw (Window* w) { MonoGlyph::Draw(w); DrawBorder(w); } Multiple decorations (ornaments)….

Page 33: Introducing Design Patterns Through a Case Study Designing A Document Editor

33

Design Issue #3Embellishing the User Interface

• To conclude Design Issue #3:– Adding additional functionalities (embellishments,

ornaments) to a class can be either achieved either via inheritance (soon you will end-up with an unmanageable hierarchy) or, better, using object composition with the help of patterns like Decorator or Chain of Responsibility (for Chain of Responsibility see also Design Issue #6)

Page 34: Introducing Design Patterns Through a Case Study Designing A Document Editor

34

Design Issue #4Supporting Multiple Look-and-Feel Standards

• One major problem in portability… consider look-and-feel for– Windows– Max OS X– KDE

• If re-targeting is too difficult (expensive), it won’t happen

• NOTE: Just one of the issues… Look-and-Feel … we deal with the Windowing system itself next

• We use an Abstract Factory Pattern• This allows us to define the product type at compile

time or run-time (based on environment or user input)

Page 35: Introducing Design Patterns Through a Case Study Designing A Document Editor

35

Design Issue #4Supporting Multiple Look-and-Feel Standards

// Creating a scrollbar…

ScrollBar* sb = guiFactory->CreateScrollBar();

Page 36: Introducing Design Patterns Through a Case Study Designing A Document Editor

36

Design Issue #4Supporting Multiple Look-and-Feel Standards

Page 37: Introducing Design Patterns Through a Case Study Designing A Document Editor

37

Design Issue #4Supporting Multiple Look-and-Feel Standards

• AbstractFactory classes are often implemented with – Factory Method pattern, or

– Prototype pattern

• Because a concrete factory is often unique in the system, it is implemented often as a Singleton.

Page 38: Introducing Design Patterns Through a Case Study Designing A Document Editor

38

Design Issue #5Supporting Multiple Window Systems

• What about the Windowing System itself? • The APIs differ… not just the visual elements

– How do we make classes from different hierarchies comply to the same abstract type?

– Can we use Abstract Factory?• Not easily… vendors already define class hierarchies

– We use Bridge pattern to • define a uniform set of windowing abstractions (common

interface)• Hide the individual implementations

Page 39: Introducing Design Patterns Through a Case Study Designing A Document Editor

39

Design Issue #5Supporting Multiple Window Systems

• Common things a Window class must do (responsibilities)– Provide operations for drawing basic geometric shapes– Maximize/Minimize– Resize– (re)Draw contents on demand (when restored,

overlapped, obscured, etc…)

• Two Possible Philosophies (Extremes)– Intersection of functionality – Only define what is

common to all– Union of functionality – Incorporate capabilities of all

systems

Page 40: Introducing Design Patterns Through a Case Study Designing A Document Editor

40

Design Issue #5Supporting Multiple Window Systems

• We adopt a hybrid

Responsibility Operations

window management

virtual void Redraw()

virtual void Raise()

virtual void Lower()

virtual void Maximize()

virtual void Minimize()

graphics virtual void DrawLine()

virutal void DrawRect()

virtual void DrawPolygon()

Virtual void DrawText()

Page 41: Introducing Design Patterns Through a Case Study Designing A Document Editor

41

Design Issue #5Supporting Multiple Window Systems

Page 42: Introducing Design Patterns Through a Case Study Designing A Document Editor

42

Design Issue #5Supporting Multiple Window Systems

Page 43: Introducing Design Patterns Through a Case Study Designing A Document Editor

43

Design Issue #5Supporting Multiple Window Systems

• Bridge pattern is used up-front in a design to let abstractions and implementations vary independently.

• On the other hand, but similar to Bridge, the Adapter pattern is geared toward making unrelated classes work together. It is usually applied to systems after they're designed, during implementation phase.

Page 44: Introducing Design Patterns Through a Case Study Designing A Document Editor

44

Design Issue #6User Operations

• Possible Operations– Document operations: create, open, save, print a document– Editing operations: select, copy, cut, paste, undo, redo– Formatting operations: text formatting, character

formatting etc– Miscellaneous operations: context sensitive help

• We have different interfaces for these operations – Different Look-and-Feel– Different Windowing Systems– Different Access Points (menu, shortcut key, context menu)

• We want independence from the UI– UI triggers the action only, but what is done should not

depend on the UI

Page 45: Introducing Design Patterns Through a Case Study Designing A Document Editor

45

Design Issue #6User Operations

• Other remarks:– The operations are implemented in many different

classes– De-coupling: we want to access the functionality

without adding dependency between the UI classes (i.e. object that issues the request) and all of the different classes involved (i.e. object that will perform the request)

– We also want to support undo and redo for some functionality

• We need to encapsulate the request using the Command Pattern

Page 46: Introducing Design Patterns Through a Case Study Designing A Document Editor

46

Design Issue #6User Operations

• Each MenuItem, ToolbarButton or KeyboardShortcut stores an appropriate command

Page 47: Introducing Design Patterns Through a Case Study Designing A Document Editor

47

Design Issue #6User Operations

…and there is an entire hierarchy of specific commands, each class (command) being in charge with a specific user operation.

Naturally, the same command object will be pointed out (shared) by different triggers, like menu items, toolbar buttons etc. Hence, no matter what triggers a user operation, the behavior is consistently the same.

Page 48: Introducing Design Patterns Through a Case Study Designing A Document Editor

48

Design Issue #6User Operations

• What about Undo? We add an Unexecute() method and keep a command history…

• …and a Memento will keep the state the command requires to undo (Unexecute) its effect.

UNDO REDO

Page 49: Introducing Design Patterns Through a Case Study Designing A Document Editor

49

Design Issue #6User Operations

• How to handle context-sensitive Help?

Page 50: Introducing Design Patterns Through a Case Study Designing A Document Editor

50

Design Issue #6User Operations

• Chain of Responsibility pattern allows to handle a request by multiple hadlers.– Intent: Avoids coupling the sender of a request to its receiver

by giving more than one object a chance to handle the request.

Page 51: Introducing Design Patterns Through a Case Study Designing A Document Editor

51

Design Issue #6User Operations

• User operations are wired into the system by the help of various Behavioral patterns, like Command, Memento or Chain of Responsibility

Page 52: Introducing Design Patterns Through a Case Study Designing A Document Editor

52

Design Issue #7Spell Check and Hyphenation

• Similar constraints to formatting• Need to support multiple algorithms• We may want to add

– search– grammar check– word count– Thesaurus– Text to speech

• This is too much for any single pattern… • There are actually two parts

– (1) Access the information– (2) Do the analysis

Page 53: Introducing Design Patterns Through a Case Study Designing A Document Editor

53

Design Issue #7Spell Check and Hyphenation – Accessing the Information

• We can encapsulate access and traversal using the Iterator pattern

Methods

void First()

void Next()

bool IsDone()

Glyph* GetCurrent()

Page 54: Introducing Design Patterns Through a Case Study Designing A Document Editor

54

Design Issue #7 Spell Check and Hyphenation – Accessing the Information

• Sample code illustrating the usage of an iterator to do our analysis

Glyph* root;

Iterator* i = root->createPreOrderIterator();

for (i->First(); !i->IsDone(); i->Next())

{

Glyph* current = i->GetCurrent();

// do some analysis

}

• Examples of iterators: an int* is an iterator for int[] type.

Page 55: Introducing Design Patterns Through a Case Study Designing A Document Editor

55

Design Issue #7 Spell Check and Hyphenation – The Analysis

• We don’t want our analysis in our iterator– Iterators can be reused

• We don’t want analysis in our Glyph class– Every time we add a new type of analysis… we have to

change our glyph classes

• Therefore– Analysis gets its own class(es)– It will use the appropriate iterator– Analyzer class accumulates data to analyze as it goes

Page 56: Introducing Design Patterns Through a Case Study Designing A Document Editor

56

Design Issue #7 Spell Check and Hyphenation – The Analysisclass SpellingChecker {public:

void Check(Glyph* glyph);};

void SpellingChecker::Check (Glyph* glyph) {

Character* c; Row* r; Image* i;

if (c = dynamic_cast<Character*>(glyph)) {

// analyze the character } else if (r = dynamic_cast<Row*>(glyph)) {

// prepare to analyze r's children } else if (i = dynamic_cast<Image*>(glyph)) {

// do nothing }

}

Glyph* root;

SpellingChecker checker;

Iterator* i = root->createPreOrderIterator();

for (i->First();!i->IsDone(); i->Next())

{

Glyph* current = i->GetCurrent();

checker.Check(current);

}

This is a start… but not what we want!

Page 57: Introducing Design Patterns Through a Case Study Designing A Document Editor

57

Design Issue #7 Spell Check and Hyphenation – The Analysis

• Why don’t we want this?– Difficult to extend: each time a new Glyph is

introduced need to change SpellingCheck::Check

– Error prone: missing one type of Glyph

– Violates several OOD principles: OCP, DRY, SRP

– Usually, the usage of dynamic_cast denotes poor OO modeling

• We want to be better than this…

Page 58: Introducing Design Patterns Through a Case Study Designing A Document Editor

58

Design Issue #7 Spell Check and Hyphenation – The Analysis• …we will use the Visitor pattern

class Visitor {

public: virtual void visitCharacter(Character*) { } virtual void visitRow(Row*) { } virtual void visitImage(Image*) { } // ... and so forth

};

• Then, we specialize this superclass into– SpellCheckingVisitor– HyphenationVisitor– and so on…

• There is an impact on Glyph hierarchy that need to be changed in order to accept visitors:– within Glyph we define an operation void visitMe(Visitor& visitor)

– Character class would call visitor.visitCharacter(this)– Row class would call visitor.visitRow(this)

Page 59: Introducing Design Patterns Through a Case Study Designing A Document Editor

59

Design Issue #7 Spell Check and Hyphenation – The Analysis

Page 60: Introducing Design Patterns Through a Case Study Designing A Document Editor

60

SummaryCovered patterns

• Very good example: 18 out of 23 patterns (78%) described in Design Patterns: Elements of Reusable Object-Oriented Software were covered within this example:

• Structural: Composite, Flyweight, Proxy, Bridge, Adapter

• Behavioral: Strategy, State, Decorator, Template Method, Command, Memento, Iterator, Visitor, Chain of Responsibility

• Creational: Singleton, Abstract Factory, Factory Method, Prototype