73
A Case Study: Designing a Document Editor ZHAO Jianhua Dept. of Computer Sci&Tech, Nanjing University

A Case Study: Designing a Document Editor

  • Upload
    ellie

  • View
    54

  • Download
    1

Embed Size (px)

DESCRIPTION

A Case Study: Designing a Document Editor. ZHAO Jianhua Dept. of Computer Sci&Tech, Nanjing University. Overview of This Case Study. A WYSIWYG document editor called Lexi. The document can mix text and graphics freely in a variety of formatting styles. Eight design patterns are illustrated. - PowerPoint PPT Presentation

Citation preview

Page 1: A Case Study: Designing a Document Editor

A Case Study:Designing a Document EditorZHAO JianhuaDept. of Computer Sci&Tech,Nanjing University

Page 2: A Case Study: Designing a Document Editor

Overview of This Case Study

A WYSIWYG document editor called Lexi.The document can mix text and graphics freely in a variety of formatting styles.Eight design patterns are illustrated.Using this case study to show What is design patterns. How to use design patterns.

Each time we meet a pattern in the case study, we will study the detail of the pattern.

Page 3: A Case Study: Designing a Document Editor

Design ProblemsSeven problems in Lexi’s design: (1,2) Document structure: The choice of internal

representation for the document. Formatting: How to arrange the text and

graphics? What objects are responsible for different formatting policies? How do these policies interact with the document’s internal representation?

Page 4: A Case Study: Designing a Document Editor

Design problemsSeven problems in Lexi’s design: (3,4) Embellishing the user interface. How to

design the system such that the embellishment can be easily added or removed?

Supporting multiple look-and-feel standards: Lexi should adapt easily to different look-and-feel standards.

Page 5: A Case Study: Designing a Document Editor

Design ProblemsSeven problems in Lexi’s design: (5,6) Supporting multiple window systems.

Lexi should be as independent of the window system as possible.

User operations. To provide a uniform mechanism both for accessing this scattered functionality and for undoing its effects.

Page 6: A Case Study: Designing a Document Editor

Design problemsSeven problems in Lexi’s design: (7) Spelling checking and hyphenation:

How does Lexi support analytical operations such as checking for misspelled words and determining hyphenation points?

Page 7: A Case Study: Designing a Document Editor

Document StructureA document is ultimately just an arrangement of basic graphical elements.An author often views these elements in terms of the physical structure: lines, columns, figures, …Lexi’s user interface should let users manipulate these structure directly.

Page 8: A Case Study: Designing a Document Editor

The goalThe internal representation should support: Maintaining the document’s physical

structure. Generating and presenting the

document visually. Mapping positions on the display to

elements in the internal representation.

Page 9: A Case Study: Designing a Document Editor

The constraintsWe should treat text and graphics uniformly.Our implementation shouldn’t have to distinguish between single elements and groups of elements in the internal representation.The need to analyze the text for such things as spelling errors and potential hyphenation.

Page 10: A Case Study: Designing a Document Editor

Recursive CompositionA common way to represent hierarchically structed information.Building increasingly complex elements out of simpler ones.characters and graphics lines column page …

Page 11: A Case Study: Designing a Document Editor

The structure of objects

Figure 2.3 Page 37

composite(column)

composite

(row)

composite

(row)

G g space Image

Page 12: A Case Study: Designing a Document Editor

Implementing the structure

Flexibility Treat the characters and graph

uniformly. Easy to extend to new character sets.Two implication: The objects need corresponding classes. The classes should have compaible

interface. (by inheritance)

Page 13: A Case Study: Designing a Document Editor

GlyphsGlyphs: an abstract class for objects in a document structure.The subclasses of Glyphs define both primitive graphical elements and structural elements.The responsibilities: How to draw themselves, what space they occupy their children and parent

Page 14: A Case Study: Designing a Document Editor

class hierarchy

Figure 2.4, Page 38

GlyphDraw()…

CharacterDraw()…char c

RectangleDraw()…

RowDraw()…Insert(Glyph g, int t)polygon

Draw()…

Page 15: A Case Study: Designing a Document Editor

Basic glyph interface

Table 2.1, Page 39

Responsibilityappearance

hit detectionstructure

Operationsvirtual void Draw(Window *)virtual void Bounds(Rect &)virtual bool Intersects(const Point&)virtual void Insert(Glyph *, int)virtual void Remove(Glyph *, int)virtual Glyph * Child(int)virtual Glyph * Parent()

Page 16: A Case Study: Designing a Document Editor

The composit PatternRecursive composition is good for more than just documents.Represent potentially complex, hirarchical structure.Composit Pattern

Page 17: A Case Study: Designing a Document Editor

2.3 FormattingThe Lexi must break text into lines, lines into columns, …Must consider: margin widths, indentation, tabulation, …Now we will consider only breaking a collection of glyphs into lines.

Page 18: A Case Study: Designing a Document Editor

Encapsulating the Formating Algorithm(to be continued)

We should consider the trade-off between the formatting speed and quality. The users may change they mind when they use it.It’s desirable to keep the algorithms independent of the document structure.We want to treat the algorithms and the Glyph subclass seperatly.

Page 19: A Case Study: Designing a Document Editor

Encapsulating the Formating Algorithm(2)

We will define a separate class hierarchy for objects that encapsulate formatting algorithms.The root of the hierarchy will define an interfact that supports a wide range of formatting algorithms.Then we can introduce a Glyph subclass that will structure its children automatically using a given algorithm object.

Page 20: A Case Study: Designing a Document Editor

Compositor and Composition

Compositor class for objects that can encapsulate a formatting algorithm. The interface let the compositor know

what and when to formate.

Responsibility OperationsWhat to formatWhen to format

void SetComposition(Composition *)virtual void Compose()

Page 21: A Case Study: Designing a Document Editor

Compositor and Composition

The glyphs it formats are the children of a special Glyph subclass called Composition.A composition gets an instance of a Compositor subclass(specialized for an algorithm) when it is created, and it tells the compositor to Compose its glyphs.

Page 22: A Case Study: Designing a Document Editor

Compositor and Composition

Composition and Compositor class relationships

Glyph

Composition

Compositor

ArrayCompositor

TeXCompositor SimpleComposit

or

children composit

orcomposition

Page 23: A Case Study: Designing a Document Editor

Composition and Compositor

When the composition needs formatting, it calls its compositor’s Compose operator.The compositor then iterates through the composition’s children and insert new Row and Column glyphs.Each Compositor subclass can implement a different linebreeaking.The compositor-Composition class split ensures a strong separation between code for document structure and code for different formatting algorithm.

Page 24: A Case Study: Designing a Document Editor

compositor-directed linebreaking

composition composito

rcolumn

row row

G g space Image

Page 25: A Case Study: Designing a Document Editor

Strategy PatternEncapsulating an algorithm in an object is the intent of the Strategy.The key participants are Strategy objects(for different algorithm) and the context in which they operate.The key to applying the Strategy pattern is designing interfaces for the strategy and its context that are general enough to support a range of algorithm.Strategy pattern.

Page 26: A Case Study: Designing a Document Editor

Embellishing the User Interface

Consider two embellishments in Lexi’s user interface Adds a border around the text editing

area to demarcate the page of text. Adds scroll bars that let the user view

different parts of the page.

Page 27: A Case Study: Designing a Document Editor

Transparent Enclosure(1)Embellishing the user interface involves extending exisiting code. Two possible ways: Inheritance and CompositionUsing inheritance may cause two major problems: Precludes rearranging embellishment in

run-time. Explosion of classes because of

combination of different embellishment.

Page 28: A Case Study: Designing a Document Editor

Transparent Enclosure(2)Composition offers a potentially more workable and flexible.We could make the embellishment an object. If we make the Glyphs contain a border,

we must modify the code of Glyphs. So we let a Border object contains a

Glyphs object.

Page 29: A Case Study: Designing a Document Editor

Transparent Enclosure(3)About Border class It should be a Glyph because it has an

appearance. Another more compelling reason:

Clients shouldn’t care whether glyphs have borders or not.

So we subclass Border from Glyph.

Page 30: A Case Study: Designing a Document Editor

Transparent Enclosure(4)The concept of transparent enclosure: simgle-child composition compatible interfaces.

A clients generally can’t tell whether they are dealing with the component or its emclosure.A enclosure may delegate all its operations ot its component or augment the behavior by doint work of its own before and/or after delegating.

Page 31: A Case Study: Designing a Document Editor

Monoglyph(1)We define MonoGlyph to serve as an abstract class for embellishment glyphs.MonoGlyph stores a reference to a component and forwards all requests to it.MonoGlyph subclasses reimplement at least one of the forwarding operations.

Page 32: A Case Study: Designing a Document Editor

MonoGlyph(2)GlyphDraw(Window)

MonoGlyphDraw(Window)

BorderDraw(Window)DrawBorder(W)

ScrollerDraw(Window)

Page 33: A Case Study: Designing a Document Editor

MonoGlyph(3)The default implementation for Draw()void MonoGlyph::Draw(Window w){_component->Draw(w);}

The Border implementation for Draw()void Border::Draw(Window w){MonoGlyph::Draw(w);DrawBorder(w);}

Page 34: A Case Study: Designing a Document Editor

MonoGlyph(4)We can add a border and a scrolling interface to Lexi’s text editing area. First compose the existing

Composition instance in a Scroller instance.

Then compose the above in a Border instance.

Page 35: A Case Study: Designing a Document Editor

Decorator PatternThe Decorator pattern captures class and object relationships that support embellishment by transparent enclosure.In decorator pattern, embellishment refers to anything that adds responsibilities to an object.Decorator Pattern

Page 36: A Case Study: Designing a Document Editor

Supporting Multiple Look-and-Feel Standards

Achieving protability across hardware and software platform is a major problem in system design.The diversity of look-and-feel standards is one of the obstacles to protability.The standards define guidelines for how applications appear and react to the user.An application that runs on more than one platform must conform to the user interface style guide on each platform.

Page 37: A Case Study: Designing a Document Editor

Supporting Multiple Look-and-Feel Standards

The design goal: make Lexi conform to multiple

existing look-and-feel standards make it easy to add support for new

standards. changing Lexi’s look and feel at run-

time.

Page 38: A Case Study: Designing a Document Editor

Abstracting Object Creation

Assumption: We have two set of widget glyph classes A set of abstract Glyph subclasses for

each category of widget glyph. For examples: ScrollBar, Button.

A set of concrete subclasses for each abstract subclass that implement different look-and-feel standards. For examples: MotifScrollBard, PMScrollBar, …

Page 39: A Case Study: Designing a Document Editor

Abstracting Object Creation(2)

Lexi must distinguish between widget for different look-and-feel styles. It must be able to instantiate a Glyph subclass for the right style.Lexi’s implementation can’t do this directly using a constructor call in C++.We have to track down and change every these call to prot Lexi to another platform.Littering our code with constructor calls to specific look&feel classes yields a maintenance nightmare.

Page 40: A Case Study: Designing a Document Editor

Abstracting Object Creation(3)

Lexi needs a way to determine the look&feel standard that’s being targeted in order to create the appropriate widgets. avoid making explicit constructor calls. bet able to replace an entire widget set

easily. We can achieve both by abstructing the

process of object creation.

Page 41: A Case Study: Designing a Document Editor

Factories and Product Classes

Two way to create an instance of Motif scroll bar glyph: ScrollBar * sb = new MotifScrollBar; ScrollBar * sb = guiFactory ->CreateScrollBar()

guiFactory is an instance of a MotifFactory class.Two way have the same effect, but there’s a crucial difference: There’s no longer anything in the code that mentions Motif by name.guiFactory can be used to manufacture a full range of widget glyphs.

Page 42: A Case Study: Designing a Document Editor

Factories and Product Classes(2)

MotifFactory is a subclass of GUIFactory, an abstract class that defines a general interface for creating widget glyphs.Subclasses of GUIFactory implement these operations to return glyphs that implement a particular look and feel.We say that factories create product objects. The products that a factory produces are related to one another.

Page 43: A Case Study: Designing a Document Editor

Factories and Product Classes(3)

The GUIFactory instance can be instantiated anywhere convenient. before it’s ever used to create

widgets after it’s clear which look and feel is

desired.

Page 44: A Case Study: Designing a Document Editor

GUIFactory class hierarchyGUIFactoryCreateScrollBar()CreateButton()…

MotifFactoryCreateScrollBar()CreateButton()…

PMFactoryCreateScrollBar()CreateButton()…

MacFactoryCreateScrollBar()CreateButton()…

Page 45: A Case Study: Designing a Document Editor

Code for using GUIFactoryIf the look&feel is know at compile-time GUIFactory* guiFactory = new MotifFactoryIf the look&feel is know at startup timeGUIFactory * guiFactory;const char * styleName = getenv(“L&F”);if(!strcmp(styleName, “Motif”)==0){guiFactory = new MotifFactory;}else if …

Page 46: A Case Study: Designing a Document Editor

Abstract Factory PatternThis pattern captures how to create families of related product objects without instantiating classes directly.It is most appropriate when the number and general kinds of product objects stay constant, and there are differences in specific product families.The Abstract Factory pattern’s emphasis on families of products distinguishes it from other creational patterns.

Page 47: A Case Study: Designing a Document Editor

Supporting Multiple Window Systems

Another portability issues: windowing environment in which Lexi runs.A platform’s window system creates the illusion of multiple overlapping windows. It manages screen space for windows and routes input to them from the keyboard and mouse.Several important and largely incompatible window systems exist today: Macintosh, Presentation Manager, Windows, X, …Make Lexi run on as many of them as possible.

Page 48: A Case Study: Designing a Document Editor

Can we use an Abstract Factory?

The constraints for windows system protability differ significantly from those for look-and-feel independence.To use Abstract Factory pattern, we must make the different widget hierarcihes to a common set of abstract product interfaces.Different window systems have incompatible programming interfaces. We can’t afford to implement our own nonstandard window system.

Page 49: A Case Study: Designing a Document Editor

Can we use an Abstract Factory?(2)

However, windows system interfaces aren’t radically different from one another. We need a uniform set of windowing abstractions that lets us take different window system implementations and slide any one of them under a common interface.

Page 50: A Case Study: Designing a Document Editor

Encapsulating Implementation Dependencies

A Window class is introduced to encapsulate the thing windows tend to do across window systems: Provide operations for drawing basic

geometric shapes. They can iconify and de-iconify

themselves. They can resize themselves They can (re)draw their contents on

demand.

Page 51: A Case Study: Designing a Document Editor

Encapsulating Implementation Dependencies

The Window class must span the functionality of windows from different window systems. Two extreme philosophies: Intersection of functionality Union of functionality

Our design will fall somewhere between the two. The Window will provide a convenient interface

that support the most popular windows features Also support the things Lexi knows about glyphs.

Page 52: A Case Study: Designing a Document Editor

Encapsulating Implementation Dependencies

Window class interface

Responsibility

Operationswindows management

virtual void Redraw();virtual void Raise();virtual void Lower();virtual void Iconify();

… …virtual void DrawLine();virtual void DrawRect();virtual void DrawPolygon();virtual void DrawText();

… …

graphics

Page 53: A Case Study: Designing a Document Editor

Encapsulating Implementation Dependencies

Window is an abstract class. Concrete subclasses of Window support the different kinds of windows that users deal with.The resulting class hierarchy gives applications like Lexi a uniform and intuitive windowing abstraction.This class hierarchy is a window interface for Lexi to work with.Our window abstraction must be implemented in terms of what the target window system provides.

Page 54: A Case Study: Designing a Document Editor

Encapsulating Implementation Dependencies

Window class hierarchy

Window

ApplicationWindow

IconWindow DialogWindow

owner

Page 55: A Case Study: Designing a Document Editor

Encapsulating Implementation Dependencies

If we encapsulate a window system’s functionality in an object, the we can implement our Window class and system’s functionality in an object, then we won’t have to change Window or any of its subclasses to support different window system.

Page 56: A Case Study: Designing a Document Editor

Window and WindowImpWe’ll define a separate WindowImp class hierarchy in which to hide different window system implementations.WindowImp is an abstract class for objects that encapsulate window system-dependent code.We configure each window object with an instance of a WindowImp subclass for that system.

Page 57: A Case Study: Designing a Document Editor

Window and WindowImp

WindowImp Hierarchy

Window

ApplicationWindow

Icon Window

Dialog Window

WindowImpDeviceRaise()DeviceRect()

Mac WindowImp

PM WindowImp

Page 58: A Case Study: Designing a Document Editor

Window and WindowImpBy hiding the implementations in WindowImp classes, we avoid polluting the Window classes with window system dependencies.Window class hierarchy comparatively small and stable.

Page 59: A Case Study: Designing a Document Editor

WindowImp SubclassesSubclasses of WindowImp convert requests into window system-specific operations.

void Rectangle::Draw (Window *w){ w->DrawRect(_x0, _y0, _x1, _y1);}

void Window::DrawRect(…){ _imp->DeviceRect(…);}

void XWindowImp::DeviceRect(…){ …, XDrawRectangle(…); }

Page 60: A Case Study: Designing a Document Editor

Configuring Windows with WindowImp

We can use Abstract Factory pattern to provide an interface for creating different kinds of window system-dependent implementation objects.

class WindowSystemFactory{public:virtual WindowImp* CreateWindowImp() = 0;virtual ColorImp * CreateColorImp() = 0;virtual FontImp * CreateFontImp() = 0;

//…}…

Page 61: A Case Study: Designing a Document Editor

Bridge PatternWindow’s interface is caters to the applications programmer, while the WindowImp’s interface caters to windows systems.Separating windowing functionality into Window and WindowImp hierarchies lets us implement and specialize these interfaces independently.The relationship between Window and WindowImp is an example of Bridge pattern.

Page 62: A Case Study: Designing a Document Editor

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

Page 63: A Case Study: Designing a Document Editor

User OperationsThe operations creating a new document, opening, saving, and printing an existing

document. cutting selected text out of the document and

pasting it back in, changing the font and style of selected text, changing the formatting of text, such as its

alignment and justification, quitting the application, and on and on.

Page 64: A Case Study: Designing a Document Editor

User operationsWe don’t want to associate a particular user operations with a particular user interface we may want multiple user interfaces to the

same operation. we may also want to change the interface in the

future. These operations are implemented in many

different classes. We don’t want to create a lot of dependencies between impl. and user interfaces.

We want to support undo and redo of most operations.

Page 65: A Case Study: Designing a Document Editor

Encapsulating a Request(1)

We could define a subclass of MenuItem for every user operation and then hard-code each subclass to carry out the request. But this approach couples the request to a particular user interface.We could parameterize MenuItem with a function to call. But this is not a complete solution.

Page 66: A Case Study: Designing a Document Editor

Encapsulating a Request(2)

Why not complete It doesn’t address the undo/redo problem It’s hard to associate state with a function.

For example, a function that changes the font needs to know which font.

Functions are hard to extend, and it’s hard to reuse.

We should parameterize MenuItems with an object. We’ll encapsulate each request in a command object.

Page 67: A Case Study: Designing a Document Editor

Command Class and Subclasses

We define a Command abstract class to provide an interface for issuing a request: A single abstract operation called “Execute”.Subclasses of Command implement Execute in different ways to fulfill different request. Some subclasses may delegate part or all of the

work to other objects. Other subclasses may be in a position to fulfill

the request. To the requester, Command objects are treated

uniformly.

Page 68: A Case Study: Designing a Document Editor

Command Class and Subclasses

Figure 2.11: Partial Command class hierarchy

CommandExecute()

PasteCmdExecute()buffer

FontCmdExecute()newFont

Page 69: A Case Study: Designing a Document Editor

Command Class and Subclasses

Figure 2.12: MenuItem-Command Relationship

Glyph

MenuItemClicked()

CommandExecute()

command->Execute()

Page 70: A Case Study: Designing a Document Editor

UndoabilityUndo/redo is an important capability in interactive applications.To undo and redo commands, we add an Unexecute operation to Command’s interface.If the net effect of executing a operations was nothing, there’s no need for a corresponding undo requrest.So to determine if a command is undoable, we add an abstract Reversible operation to the Command interface.

Page 71: A Case Study: Designing a Document Editor

Command HistoryThe final step in supporting arbitrary-level undo and redo is to define a command history.Conceptually, the command history is a list of Command objects.The redo and undo functionality can be done by shift in this list.

Page 72: A Case Study: Designing a Document Editor

Command History

Undo and redo

present

present

Unexecute

present

execute

present

Page 73: A Case Study: Designing a Document Editor

Command PatternCommand pattern describes how to encapsulate a request.The command pattern prescribes a uniform interface for issuing requests that lets you configure clients to handle different requests.This is perfect for thou applications like Lexi that must provide centralized access to functionality scattered throughout the application.The Command pattern