0% found this document useful (0 votes)
185 views30 pages

Lexi Document Editor Design Overview

This document discusses software design patterns for creating a document editor called Lexi that supports multiple look-and-feel standards. It describes using the Abstract Factory pattern to abstract object creation and allow Lexi to be independent of how products like buttons and scroll bars are created. The Abstract Factory pattern defines interfaces for creating families of related objects without specifying their concrete classes, making it easier to retarget Lexi to different platforms. The document explains how abstract factories, products, and concrete factories collaborate to isolate concrete classes and make exchanging product families straightforward.

Uploaded by

usama sarfraz
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
185 views30 pages

Lexi Document Editor Design Overview

This document discusses software design patterns for creating a document editor called Lexi that supports multiple look-and-feel standards. It describes using the Abstract Factory pattern to abstract object creation and allow Lexi to be independent of how products like buttons and scroll bars are created. The Abstract Factory pattern defines interfaces for creating families of related objects without specifying their concrete classes, making it easier to retarget Lexi to different platforms. The document explains how abstract factories, products, and concrete factories collaborate to isolate concrete classes and make exchanging product families straightforward.

Uploaded by

usama sarfraz
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Software Design and Architecture

Week 5

A Case Study: Designing a Document Editor - Lexi

Week 2 1
Supporting Multiple Look-and-Feel Standards
● Achieving portability across hardware and software platforms is a
major problem in system design. Retargeting Lexi to a new
platform shouldn't require a major overhaul, or it wouldn't be
worth retargeting. We should make porting as easy as possible.
● One obstacle to portability is the diversity of look-and-feel
standards, which are intended to enforce uniformity between
applications. An application that runs on more than one platform
must conform to the user interface style guide on each platform.

Week 2 2
Supporting Multiple Look-and-Feel Standards
● Our design goals are to make Lexi conform to multiple existing
look-and-feel standards and to make it easy to add support for
new standards as they (invariably) emerge. We also want our
design to support the ultimate in flexibility: changing Lexi's look
and feel at run-time.

Week 2 3
Abstracting Object Creation
● Everything we see and interact with in Lexi's user interface is a
glyph composed in other, invisible glyphs like Row and Column.
● The invisible glyphs compose visible ones like Button and
Character and lay them out properly.
● "Widgets," another term for visible glyphs like buttons, scroll
bars, and menus that act as controlling elements in a user
interface. Widgets might use simpler glyphs such as characters,
circles, rectangles, and polygons to present data.

Week 2 4
Abstracting Object Creation
● We'll assume we have two sets of widget glyph classes with which to
implement multiple look-and-feel standards:
1)A set of abstract Glyph subclasses for each category of widget glyph. For
example, an abstract class ScrollBar will augment the basic glyph
interface to add general scrolling operations; Button is an abstract class
that adds button-oriented operations; and so on.
2)A set of concrete subclasses for each abstract subclass that implement
different look-and-feel standards. For example, ScrollBar might have Motif
ScrollBar and PMScrollBar subclasses that implement Motif and
Presentation Manager-style scroll bars, respectively.

Week 2 5
Abstracting Object Creation
● Lexi must distinguish between widget glyphs for different look-
and-feel styles. For example, when Lexi needs to put a button in
its interface, it must instantiate a Glyph subclass for the right
style of button (MotifButton, PMButton, MacButton, etc.).
● Lexi's implementation can't do this directly, say, using a
constructor call in C++. That would hard-code the button of a
particular style, making it impossible to select the style at run-
time. We'd also have to track down and change every such
constructor call to port Lexi to another platform.

Week 2 6
Abstracting Object Creation
● Not only must we avoid making explicit constructor calls; we must also be
able to replace an entire widget set easily. We can achieve both by
abstracting the process of object creation.
● Normally we might create an instance of a Motif scroll bar glyph with the
following C++ code:
ScrollBar* sb = new MotifScrollBar;
● This is the kind of code to avoid if you want to minimize Lexi's look-and-
feel dependencies. But suppose we initialize sb as follows:
ScrollBar* sb = guiFactory→CreateScrollBar();
where guiFactory is an instance of a MotifFactory class.
Week 2 7
Abstracting Object Creation
● There's no longer anything in the code that mentions Motif by
name.
● The guiFactory object abstracts the process of creating not just
Motif scroll bars but scroll bars for any look-and-feel standard.
● And guiFactory isn't limited to producing scroll bars. It can
manufacture a full range of widget glyphs, including scroll bars,
buttons, entry fields, menus, and so forth.

Week 2 8
Abstracting Object Creation
● All this is possible because MotifFactory is a subclass of GUIFactory, an
abstract class that defines a general interface for creating widget glyphs.
● It includes operations like CreateScrollBar and CreateButton for
instantiating different kinds of widget glyphs.
● Subclasses of GUIFactory implement these operations to return glyphs
such as MotifScrollBar and PMButton that implement a particular look and
feel.
● We say that factories create product objects. Moreover, the products that a
factory produces are related to one another; in this case, the products are
all widgets for the same look and feel.

Week 2 9
Abstracting Object Creation

Week 2 10
Abstracting Object Creation

Week 2 11
Abstracting Object Creation

Week 2 12
Abstract Factory (87) Pattern
● Factories and products are the key participants in the Abstract
Factory (87) pattern. This pattern captures how to create families
of related product objects without instantiating classes directly.
Intent
● Provide an interface for creating families of related or dependent
objects without specifying their concrete classes.
● Clients aren't aware of the concrete classes they're using. Thus
clients stay independent of the concrete classes.

Week 2 13
Abstract Factory (87)

Week 2 14
Abstract Factory (87) - Applicability
Use the Abstract Factory pattern when
● a system should be independent of how its products are created,
composed, and represented.
● a system should be configured with one of multiple families of products.
● a family of related product objects is designed to be used together, and
you need to enforce this constraint.
● you want to provide a class library of products, and you want to reveal
just their interfaces, not their implementations.

Week 2 15
Abstract Factory (87) - Applicability

Week 2 16
Abstract Factory (87) - Participants
● AbstractFactory (WidgetFactory)
– declares an interface for operations that create abstract product objects.
● ConcreteFactory (MotifWidgetFactory, PMWidgetFactory)
– implements the operations to create concrete product objects.
● AbstractProduct (Window, ScrollBar)
– declares an interface for a type of product object.
● ConcreteProduct (MotifWindow, MotifScrollBar)
– defines a product object to be created by the corresponding concrete factory.
– implements the AbstractProduct interface.
● Client
– uses only interfaces declared by AbstractFactory and AbstractProduct classes.
Week 2 17
Abstract Factory (87) - Collaborations
● Normally a single instance of a ConcreteFactory class is created
at run-time. This concrete factory creates product objects having
a particular implementation. To create different product objects,
clients should use a different concrete factory.
● AbstractFactory defers creation of product objects to its
ConcreteFactory subclass.

Week 2 18
Abstract Factory (87) - Consequences
It isolates concrete classes.
● Because a factory encapsulates the responsibility and the
process of creating product objects, it isolates clients from
implementation classes.
● Clients manipulate instances through their abstract interfaces.
● Product class names are isolated in the implementation of the
concrete factory; they do not appear in client code.

Week 2 19
Abstract Factory (87) - Consequences
● It makes exchanging product families easy.
● The class of a concrete factory appears only once in an application—that
is, where it's instantiated. This makes it easy to change the concrete
factory an application uses. It can use different product configurations
simply by changing the concrete factory.
● Because an abstract factory creates a complete family of products, the
whole product family changes at once. In our user interface example, we
can switch from Motif widgets to Presentation Manager widgets simply
by switching the corresponding factory objects and recreating the
interface.

Week 2 20
Abstract Factory (87) - Consequences
It promotes consistency among products.
● When product objects in a family are designed to work together,
it's important that an application use objects from only one family
at a time. AbstractFactory makes this easy to enforce.

Week 2 21
Abstract Factory (87) - Consequences
Supporting new kinds of products is difficult.
● Extending abstract factories to produce new kinds of Products isn't
easy.
● That's because the AbstractFactory interface fixes the set of products
that can be created. Supporting new kinds of products requires
extending the factory interface, which involves changing the
AbstractFactory class and all of its subclasses.
● We discuss one solution to this problem in the Implementation
section.

Week 2 22
Abstract Factory (87) - Implementation
Factories as singletons.
● An application typically needs only one instance of a
ConcreteFactory per product family. So it's usually best
implemented as a Singleton (127).

Week 2 23
Abstract Factory (87) - Implementation
Creating the products.
● AbstractFactory only declares an interface for creating products. It's up to
ConcreteProduct subclasses to actually create them. The most common way
to do this is to define a factory method (see Factory Method (107)) for each
product. A concrete factory will specify its products by overriding the factory
method for each.
● If many product families are possible, the concrete factory can be implemented
using the Prototype (117) pattern. The concrete factory is initialized with a
prototypical instance of each product in the family, and it creates a new
product by cloning its prototype. The Prototype-based approach eliminates the
need for a new concrete factory class for each new product family.

Week 2 24
Abstract Factory (87) - Implementation
Defining extensible factories.
● AbstractFactory usually defines a different operation for each kind of product it can produce - encoded in
the operation signatures. Adding a new kind of product requires changing the AbstractFactory interface
and all the classes that depend on it.
● A more flexible but less safe design is to add a parameter to operations that create objects. This
parameter specifies the kind of object to be created. It could be a class identifier, an integer, a string, or
anything else that identifies the kind of product. In fact with this approach, AbstractFactory only needs a
single "Make" operation with a parameter indicating the kind of object to create. This is the technique
used in the Prototype- and the class-based abstract factories discussed earlier.
● An inherent problem remains: All products are returned to the client with the same abstract interface as
given by the return type. The client will not be able to differentiate or make safe assumptions about the
class of a product. If clients need to perform subclass-specific operations, they won't be accessible
through the abstract interface.
● Although the client could perform a downcast (e.g., with dynamic-cast in C++), that's not always feasible
or safe, because the downcast can fail. This is the classic trade-off for a highly flexible and extensible
interface.
Week 2 25
Abstract Factory (87) – Sample Code

Week 2 26
Abstract Factory (87) – Sample Code

Week 2 27
Abstract Factory (87) – Sample Code

Week 2 28
Abstract Factory (87) – Sample Code

Week 2 29
Abstract Factory (87) – Sample Code

Week 2 30

You might also like