a case study: designing a document editor

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

Upload: mara-randolph

Post on 01-Jan-2016

51 views

Category:

Documents


2 download

DESCRIPTION

A Case Study: Designing a Document Editor. Linzhang Wang 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. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: A Case Study: Designing a Document Editor

Linzhang Wang

Dept. of Computer Sci&Tech,

Nanjing University

A Case Study:Designing a Document Editor

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 Problems

Seven 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 problems

Seven 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 Problems

Seven 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 problems

Seven 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 Structure

A 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 goal

The 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 constraints

We should treat text and graphics uniformly. Our implementation should not 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 Composition

A common way to represent hierarchically struck ed 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 compatible interface. (by

inheritance)

Page 13: A Case Study: Designing a Document Editor

Glyphs

Glyphs: 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

Glyph

Draw()…

CharacterDraw()…

char c

RectangleDraw()…

Row

Draw()…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 detection

structure

Operations

virtual 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 composite Pattern

Recursive composition is good for more than just documents.

Represent potentially complex, hierarchical structure.

Composite Pattern

Page 17: A Case Study: Designing a Document Editor

2.3 Formatting

The 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 Formatting 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 separately.

Page 19: A Case Study: Designing a Document Editor

Encapsulating the Formatting Algorithm(2)

We will define a separate class hierarchy for objects that encapsulate formatting algorithms.

The root of the hierarchy will define an interface 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 format.

Responsibility Operations

What 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

SimpleCompositor

children composit

or

composition

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

compositorcolumn

row row

G g space Image

Page 25: A Case Study: Designing a Document Editor

Strategy Pattern

Encapsulating 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 existing code. Two possible ways: Inheritance and Composition

Using 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’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: single-child composition compatible interfaces.

A clients generally can’t tell whether they are dealing with the component or its enclosure.

A enclosure may delegate all its operations to its component or augment the behavior by doing 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 re-implement at least one of the forwarding operations.

Page 32: A Case Study: Designing a Document Editor

MonoGlyph(2)

Glyph

Draw(Window)

MonoGlyph

Draw(Window)

Border

Draw(Window)

DrawBorder(W)

Scroller

Draw(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 Pattern

The 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 portability 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 portability.

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 port 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. be able to replace an entire widget set easily. We can achieve both by abstracting 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 hierarchy

GUIFactory

CreateScrollBar()CreateButton()…

MotifFactory

CreateScrollBar()CreateButton()…

PMFactory

CreateScrollBar()CreateButton()…

MacFactory

CreateScrollBar()CreateButton()…

Page 45: A Case Study: Designing a Document Editor

Code for using GUIFactory

If the look&feel is know at compile-time GUIFactory* guiFactory = new MotifFactory

If 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 Pattern

This 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 portability differ significantly from those for look-and-feel independence.

To use Abstract Factory pattern, we must make the different widget hierarchies 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

Operations

windows 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 WindowImp

We’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

WindowImp

DeviceRaise()

DeviceRect()

Mac WindowImp

PM WindowImp

Page 58: A Case Study: Designing a Document Editor

Window and WindowImp

By 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 Subclasses

Subclasses 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 Pattern

Window’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 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 windowing abstractions without touching window system dependent code, and vice versa.

Bridge Pattern

Page 63: A Case Study: Designing a Document Editor

User Operations

The 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 operations

We 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

Command

Execute()

PasteCmd

Execute()

buffer

FontCmd

Execute()

newFont

Page 69: A Case Study: Designing a Document Editor

Command Class and Subclasses

Figure 2.12: MenuItem-Command Relationship

Glyph

MenuItemClicked()

Command

Execute()

command->Execute()

Page 70: A Case Study: Designing a Document Editor

Undoability

Undo/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 History

The 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 Pattern

Command 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