I don't seem to have write permissions to the entries directory. Here's the structured summary for this source document:
---
GoF Case Study: Decorator, Abstract Factory, and Bridge Patterns in Document Editor Design
This section from the GoF *Design Patterns* case study (Lexi document editor, Sections 2.4–2.6) addresses three design problems — embellishing the user interface, supporting multiple look-and-feel standards, and supporting multiple window systems — each solved by a different pattern. The Decorator pattern wraps glyphs with transparent embellishments (borders, scrollbars). The Abstract Factory pattern creates families of platform-specific widgets without hard-coding concrete classes. The Bridge pattern decouples a Window abstraction from its platform-specific implementation (WindowImp).
Key Concepts
Transparent Enclosure: Combines single-child composition with compatible interfaces so clients cannot distinguish a decorated object from an undecorated one. The enclosure delegates all operations to its component and may augment behavior before or after delegation.
MonoGlyph: An abstract class serving as the base for embellishment glyphs. Stores a reference to a single component and forwards all requests to it by default, making it fully transparent to clients.
Embellishment via object composition vs. inheritance: Inheritance-based embellishment (BorderedComposition, ScrollableComposition, BorderedScrollableComposition) causes a combinatorial explosion of classes. Object composition avoids this by wrapping at run-time.
Border::Draw extends rather than replaces: It calls MonoGlyph::Draw(w) first (letting the component draw itself), then calls DrawBorder(w). This is extension of parent behavior, not replacement.
Order of composition matters: Composing a Composition inside a Scroller inside a Border produces different behavior than Composition inside Border inside Scroller (the border scrolls with the text in the latter case).
Embellishment kept separate from structural composition: MonoGlyph wraps a single child; multi-child composition (Row, Column) is handled by separate classes. This keeps embellishment classes simple and avoids replicating composition functionality.
Abstract Factory for look-and-feel portability: A GUIFactory abstract class declares CreateScrollBar(), CreateButton(), etc. Concrete factories (MotifFactory, PMFactory) return platform-specific widgets. Client code never mentions concrete widget classes by name.
Factory initialization strategies: Can be a global variable, static member, or local variable. Initialized at compile-time (new MotifFactory), via environment variable/string lookup, or via a registry that maps strings to factory objects (most extensible — avoids linking all platform factories).
Swapping entire product families: Replacing the concrete factory instance with a different one changes all widgets at once — the Abstract Factory pattern's distinguishing feature among creational patterns.
Window abstraction vs. WindowImp: The Window class provides a stable, application-facing interface for drawing and window management. WindowImp is a separate hierarchy that encapsulates platform-specific window system code.
Bridge pattern motivation: Neither intersection-of-functionality (least-common-denominator) nor union-of-functionality (huge, unstable) extremes work. The Bridge decouples the abstraction from its implementation so both can vary independently.
Window delegates to WindowImp: Window::DrawRect() calls _imp->DeviceRect(). Each WindowImp subclass (XWindowImp, PMWindowImp) translates to platform-native calls (XDrawRectangle for X, GpiBeginPath/GpiPolyLine/GpiStrokePath for PM).
WindowImp configured via Abstract Factory: A WindowSystemFactory creates WindowImp, ColorImp, FontImp objects. The Window constructor uses this factory to initialize its _imp member, keeping platform selection centralized.
Commands and Syntax
// MonoGlyph forwarding (transparent delegation)
void MonoGlyph::Draw(Window* w) {
_component->Draw(w);
}
// Border extending parent behavior
void Border::Draw(Window* w) {
MonoGlyph::Draw(w); // let component draw first
DrawBorder(w); // then draw the border
}
// Abstract Factory: creating widgets without naming concrete classes
ScrollBar* sb = guiFactory->CreateScrollBar();
// instead of: ScrollBar* sb = new MotifScrollBar;
// Factory initialization via environment variable
GUIFactory* guiFactory;
const char* styleName = getenv("LOOK_AND_FEEL");
if (strcmp(styleName, "Motif") == 0) {
guiFactory = new MotifFactory;
} else if (strcmp(styleName, "Presentation_Manager") == 0) {
guiFactory = new PMFactory;
} else {
guiFactory = new DefaultGUIFactory;
}
// Bridge: Window delegates drawing to WindowImp
void Window::DrawRect(Coord x0, Coord y0, Coord x1, Coord y1) {
_imp->DeviceRect(x0, y0, x1, y1);
}
// WindowImp subclass translates to platform-specific API
void XWindowImp::DeviceRect(Coord x0, Coord y0, Coord x1, Coord y1) {
int x = round(min(x0, x1));
int y = round(min(y0, y1));
int w = round(abs(x0 - x1));
int h = round(abs(y0 - y1));
XDrawRectangle(_dpy, _winid, _gc, x, y, w, h);
}
// WindowSystemFactory for creating platform-specific implementations
class WindowSystemFactory {
public:
virtual WindowImp* CreateWindowImp() = 0;
virtual ColorImp* CreateColorImp() = 0;
virtual FontImp* CreateFontImp() = 0;
};
// Window constructor uses factory to get correct implementation
Window::Window() {
_imp = windowSystemFactory->CreateWindowImp();
}
Relationships
Decorator pattern generalizes transparent enclosure — applies beyond UI embellishment to any situation where responsibilities are added dynamically (AST semantic actions, FSA transitions, persistent object tags).
Abstract Factory is applied twice: once for widget creation (GUIFactory) and once for window system implementation creation (WindowSystemFactory). The case study shows when Abstract Factory does NOT work: when existing vendor hierarchies lack common abstract product classes.
Bridge pattern separates abstraction (Window hierarchy) from implementation (WindowImp hierarchy). WindowImp's interface reflects what window systems actually provide; Window's interface reflects what application programmers need.
Singleton is referenced for managing well-known one-of-a-kind objects like guiFactory.
Strategy (Section 2.3) used a similar encapsulation approach — the recurring theme is "encapsulate the concept that varies."
Decorator wraps single objects; structural Composition (Row, Column) handles multi-child layout — deliberately separate concerns.
Exam-Relevant Points
Decorator vs. inheritance: Inheritance causes class explosion (one per combination); Decorator composes at run-time. Decorator = single-child composition + compatible interfaces (transparent enclosure).
MonoGlyph::Draw forwards; Border::Draw extends (calls super then adds behavior). Extending parent behavior is distinct from replacing it.
Abstract Factory creates families of related products — emphasis on families distinguishes it from other creational patterns. Swapping the concrete factory swaps the entire product family.
Abstract Factory requires common abstract product classes — it fails when existing vendor hierarchies don't share a common interface (motivating Bridge instead).
Bridge separates abstraction from implementation so both hierarchies can vary independently.
Bridge uses delegation: Window holds a _imp pointer to WindowImp and delegates platform-specific operations.
DeviceRect can have radically different implementations across platforms (X uses XDrawRectangle directly; PM uses a multi-step path-based API) — Bridge hides this.
Abstract Factory composes with Bridge: WindowSystemFactory creates the correct WindowImp, which Window stores as _imp. Two patterns working together.
Recurring design principle: "Encapsulate the concept that varies" — applied to formatting (Strategy), embellishment (Decorator), widget creation (Abstract Factory), and window system implementation (Bridge).