Skip to main content
Object Oriented Design, Analysis and Programming

Designing with Patterns — Decorator, Command, Proxy, Strategy, and Factory

Published: 2026-08-19
Level: postgraduate
Audience: Postgraduate students in Object Oriented Design, Analysis and Programming

Prerequisite Knowledge

This lecture builds on the following concepts from earlier lectures. If any feel unfamiliar, review the linked notes before proceeding.

Previously Covered in This Subject

  • Adapter — Using What You Have with the Interface You Need — covered in Lecture 13: Design Patterns — Gang of Four Solutions
  • Granularity — Where Patterns Sit and Design Pattern Foundations — covered in Lecture 13: Design Patterns — Gang of Four Solutions
  • Design Patterns as Repeatable Solutions — covered in Lecture 1: Object-Oriented Analysis and Design

# Designing with Patterns — Decorator, Command, Proxy, Strategy, and Factory

14.1 Good Design, Pattern Granularity, and Adapter Recap

14.1.1 Why Patterns and What Counts as Good Design

Hook — can you judge a design without personal taste? If two students draw completely different class diagrams for the same sales system, how do you decide which one is better without saying "I just like this one"? Patterns give you a repeatable yardstick.

Design for object-oriented systems aims to standardize how a system is built so that design choices become repeatable and reviewable. A natural opening question is how to say a design is good and whether we can set criteria for that judgment. Patterns help answer it. They capture proven arrangements of classes and objects that improve cohesion — how focused a class is on one job — , low coupling — how little classes depend on each other's internals — , reuse, and clarity, and they give a shared vocabulary for discussing alternatives in any design problem.

Intuition — patterns as building grammar. Think of patterns like the grammar of a language. A single sentence never defines English grammar, and two sentences that look alike — "The cat sat" versus "The hat sat" — can have wildly different meanings based on one word. In the same way, a single code example never defines a pattern, and two patterns whose diagrams look identical, like Decorator and Composite, mean different things because their intent differs. The analogy holds for the core relationship: grammar maps words to meaning; patterns map structure to intent. It breaks where human language tolerates ambiguity and code does not — a wrong intent still compiles but silently creates the wrong design.

Formalize — what a pattern is and is not. A design pattern is a named, proven solution template for a recurring design problem, described by its intent (the problem and forces), structure (participants and collaborations), and consequences (trade-offs). It is not a library, not a framework, and not copy-paste code.

Patterns operate at a medium level of granularity — larger than a single class or idiom, smaller than an architecture or framework. They are vague concepts in the sense that a single example never fully defines a pattern, and patterns that look very similar can have different intent. That vagueness is intentional: patterns describe roles and collaborations, not exact names or line counts, so you must adapt them.

Learning therefore proceeds in two stages that the lecture emphasized explicitly: first learn each pattern in isolation with its specific intent, structure, and trade-offs, and only then combine multiple patterns in a single design problem. The skill lies in segregating patterns that appear similar and matching the intent to the problem, not the diagram alone. Trying to learn three patterns at once guarantees confusion because your brain matches the boxes and lines, not the purpose.

Visual intuition: imagine two axes. The horizontal axis is granularity from "single statement" on the left to "whole application architecture" on the right. The vertical axis is reuse frequency. Idioms sit low-left, patterns sit in the middle-high hump — used in almost every project but never as a whole system — and frameworks sit far right. The takeaway: you will reach for patterns daily, but you will never build a system that is just one pattern.

Scope — when patterns apply and when they do not. Assumption: a recurring problem with competing forces — for example, need for flexibility versus need for simplicity. When it holds: using a pattern standardizes the solution and communicates intent to teammates. When it fails: if the problem is one-off, or flexibility is not needed, a pattern adds indirection, extra classes, and indirection cost for no benefit. A pattern applied without a force to resolve is over-engineering — it lowers cohesion and increases coupling to abstract types that never vary.

Pitfalls — first-day traps with pattern granularity.

  • Diagram matching: seeing the same UML shape and assuming the same pattern. Decorator versus Composite is the classic exam trap — shape is identical, intent is opposite.
  • Premature combining: trying to apply three patterns before mastering one in isolation. The lecture warns to keep them segregated initially.
  • Vocabulary without mechanics: naming a class SomethingAdapter without actually translating an interface. Naming never satisfies a pattern; behavior does.

Real-world and domain connection: In the NextGen POS case study developed throughout the course, tax calculators, credit authorization, inventory and accounting are all external services with incompatible APIs. The team does not write four different sales logics; they apply Adapter to present one stable ITaxCalculatorAdapter interface and Factory plus Singleton to create it. The domain benefit is immediate: the POS can switch from TaxMaster to GoodAsGoldTaxPro by changing a property file, with zero edits to Sale or Register. The same separation-of-concepts reasoning applies in any product line that must plug in external vendors.

Recap — good design is not taste; it is measurable through cohesion, low coupling, and protected variation, and patterns are the medium-granularity vocabulary that makes those judgments repeatable. Bridge — with that yardstick in place, the lecture re-anchors on the most recent pattern you already know so the new patterns have a contrast point: the Adapter.

14.1.2 Adapter Recap — Identifying the Need

Structure — Adapter in one paragraph. The Adapter answers: you want to use the services of a class in the current context, but its interface does not match what the client requires, even though the underlying functionality does match. You do not rewrite the adaptee. You insert an intermediate adapter object that implements the target interface the client expects and holds a reference to the adaptee, translating each target method call into the corresponding adaptee call. In GRASP terms, it is a Pure Fabrication plus Indirection that achieves Protected Variation via Polymorphism.

Participants in words: Target is the interface the client depends on — for example ICan in the lecture's shorthand or ITaxCalculatorAdapter with getTaxes(Sale). Adaptee is the existing class with the useful behavior but the wrong interface — for example GoodAsGoldTaxPro or the external SAPSystem via SOAP over HTTPS. Adapter implements Target and forwards targetMethod() to adaptee.adapteeMethod() with argument and return translation. The client only sees Target.

Real-world anchor: power adapters convert one kind of plug and socket into another so an otherwise usable device can connect. The same idea holds in code: the source interface and the target interface needed by the client do not match, yet the underlying context and functionality are the same. The conversion is purely interfacial — no change to original functionality. The adapter converts from target to source by translating calls, adapting one interface to the required target interface while leaving the adaptee unchanged.

The problem description that points to Adapter therefore mentions an existing class whose behavior is exactly what is needed, but whose method names or signatures are incompatible with what the client expects. The solution is an intermediate converter that forwards target calls to the adaptee. A concrete illustration used last time involved an interface called ICan that needed to be used through a target container interface. The adapter translates every method of the target container interface into a call on the ICan interface.

Visual intuition: draw two rectangles, left is Target socket with three pins, right is Adaptee plug with two flat blades. The Adapter sits between them. Its left face has three holes, its right face has two slots, and inside there are three wires that re-route to the two slots plus a small transformer. Both axes are interface shape versus function: shape differs, function is preserved, and the takeaway is that only the shape changes.

Scope — when Adapter fits and when to choose something else. Assumption: the adaptee's behavior is exactly what you need; only naming, parameter order, return type, or protocol differs. When it applies: integrating third-party libraries, legacy code, or subsystems you cannot edit. When it breaks: if behavior is missing or semantics differ — for example the adaptee computes inclusive tax while you need exclusive tax — translation cannot fix it; you need a different strategy or a wrapper that adds logic, not just forwarding. Adapter also does not add new responsibility; if you are adding scrolling or lazy loading, consider Decorator or Proxy instead.

14.1.3 Student Questions and Answers

Q: How do we know Adapter should be applied when reading a problem? A: Check two conditions that must both be true. First, there is a class whose services are wanted in the current context — the context is the same, not a different domain. Second, its interface does not match the interface the client requires — method names, signatures, or protocol mismatch. If both hold and the mismatch is only at the interface level, wrap the existing class with an adapter that translates target methods to source methods. The real-world test is the socket versus plug mismatch — you do not rewrite the device, you convert the interface. A helpful second test: the examiner's phrasing will mention "existing class," "reuse without modification," "incompatible interface," or "convert target to source." If instead the phrasing is "add scrolling to any component without editing it" that is Decorator; "defer creation until first use" is Proxy. Frequency note: several students ask the Adapter identification question in this lecture and in pattern labs; the two-condition check above is the canonical answer the professor repeats.

Exam note: Adapter identification is a high-frequency exam mapping. Expect phrasing like "convert plug into socket," "existing services not compatible," "translate every method of the target container interface into a call on ICan." The answer must state both conditions, name the target and adaptee, and stress that functionality is unchanged — only the interface is converted. Quick check: given a new tax calculator NewTaxPro with method computeTaxes(Sale s, boolean inclusive) versus your ITaxCalculatorAdapter.getTaxes(Sale), what would the Adapter's getTaxes do internally? Answer: call newTaxPro.computeTaxes(s, false) and translate the returned List<TaxLineItem> format if needed.

14.2 Decorator Pattern — Adding Visual and Behavioral Enhancement Without Changing Core Behavior

14.2.1 Intent and Problem Context

Hook — every widget suddenly needs a scrollbar. The text area overflows, the button label overflows, the panel overflows. Do you open ten classes and paste the same scrollbar code into each one?

The decorator answers the problem of adding functionality to many kinds of components without editing each component class. A common motivating example is scroll bars. Whenever a component becomes larger than the screen, horizontal and vertical scroll bars are needed. The same need appears for text areas, buttons, panels, and other widgets.

One naive solution is to add scroll bar logic inside every component class — a text area with scrolling, a button with scrolling, and so on. That approach duplicates code and forces a change to every class whenever a new kind of decoration is required, violating the Open-Closed Principle. A better solution keeps the core component unchanged and adds enhancements around it. Enhancement or addition of functionality while the basic functionality stays the same is called decoration. Responsibility for decoration is kept outside the component so the component stays closed for modification yet open for extension.

Intuition — Diwali house. Think of decoration the way a house is decorated for Diwali — the house remains a house, it is still lived in the same way, but lights, colors, and ornaments enhance it. The mapping is explicit: house = ConcreteComponent such as JTextArea, foundation and walls = core paint() and layout behavior, lights and torans = ConcreteDecorator such as JScrollPane or BorderDecorator, living in the house unchanged = client code still calling paint() or adding the object to a JFrame. The break point: real lights consume electricity and can fail independently; a Decorator's extra behavior executes only when the wrapped method is called and shares the component's lifecycle — you cannot "switch off" the house and keep the lights.

Real-world: the Java Swing library uses this pattern heavily. A GUI application built with Swing contains many decorators. The class JScrollPane decorates a component such as a JTextArea, a JButton, or any JComponent. After decoration, the result must still behave as a component so it can be placed in containers and treated uniformly. That is the central test the professor repeats: after decoration the object still behaves as a component.

Scope — when Decorator applies. Assumption: you need to add responsibilities to individual objects, not to every instance of a class, and the set of possible decorations is open-ended and not known at class-design time. When it holds: visual or behavioral enhancements like borders, scrolling, shadows, or stream buffering that vary per instance. When it fails: if every instance needs the same new behavior, subclassing is simpler; if you need to add the same decoration to many objects cheaply and want to share intrinsic state, consider Flyweight; if you need to change the interface, you need Adapter, not Decorator.

14.2.2 Structure, Participants, and How Decoration Combines

Formalize — participants and the combine rule. Participants are an abstraction for the component, the concrete components that implement that abstraction, and the decorators that also realize the same abstraction.

  • Component — interface or abstract class that declares operations such as paint(Graphics g) or draw() or getText().
  • ConcreteComponent — for example JTextArea or more generally JComponent — implements Component with core behavior.
  • Decorator — abstract class that also implements Component, aggregates a Component instance (decorated: Component), and forwards calls to it.
  • ConcreteDecorator — for example JScrollPane, BorderDecorator, SlashDecorator — adds its own state and behavior around the forwarded call.

Structure in words: Component declares operation(). ConcreteComponent implements operation() directly. Decorator implements Component, holds component: Component via aggregation, and its operation() calls component.operation() then augments. ConcreteDecorator overrides operation() to do its own work before or after forwarding.

The most important implementation rule is that when a decorator implements a method from the Component interface, it applies that method to the decorated component and combines the result with the effect of decoration. Augmenting means enhancing, not replacing. For instance, a paint(Graphics g) method in JScrollPane will call paint(g) on the wrapped text area and then add scroll bar rendering and scrolling behavior around it. Textbook phrasing from the companion Decorator chapter: you favor object containment over inheritance — the Decorator is a JComponent so it can receive paint calls, but it contains the component it decorates and forwards to it.

Pseudocode that captures the forwarding idiom:

abstract class Decorator implements Component {
    protected Component wrapped;
    Decorator(Component c) { this.wrapped = c; }
    void operation() { wrapped.operation(); // then add decoration }
}
class JScrollPaneDecorator extends Decorator {
    void paint(Graphics g) {
        wrapped.paint(g);          // 1. render core text
        paintScrollBars(g);         // 2. add scroll decoration
        clipToViewport(g);
    }
}

Because both the component and the decorator share the same abstraction, decorating produces another component. That property allows any number of decorations to be stacked. You can start with a text area, wrap it in a scroll pane, then add a border, a color tint, a shadow, or any other visual or behavioral enhancement. Each new decorator wraps the previous result, and the outermost object still responds as a component. This supports an open-ended set of possible decorations that are not known when the component class is written. The Java Swing idiom new JScrollPane(myTextArea) is exactly this: the pane's constructor takes a JComponent and, because JComponent itself is a Container, it can add the wrapped component to itself and forward all GUI method calls.

The companion text shows the chaining explicitly: jp.add(new SlashDecorator(new CoolDecorator(new JButton("Dbutton")))); — a button decorated first as a cool-button that erases borders until mouse-over, then with a red diagonal line. Each layer intercepts paint and setBounds, saves state, calls super.paint(g), then draws its own embellishment. The setBounds interception matters because a decorator must remember the extent of the object it decorates.

Visual intuition: draw a set of concentric rectangles. Innermost is JTextArea with text. Next ring is JScrollPane adding vertical and horizontal bars on the right and bottom edges. Next ring is BorderDecorator adding a 2-pixel line border plus 4-pixel empty inset. The x-axis is nesting depth, the y-axis is visual responsibility added. The shape is additive stairs — each ring adds one feature without changing the inner rectangle's identity. The takeaway: transparency — the outermost ring still presents the Component interface, so any client expecting a Component cannot tell how many layers are inside.

Assumptions & scope — transparency limits. Assumption: the decorator is transparent — decorator instanceof Component is true and identity is preserved for interface calls. When it breaks: type tests fail — a JScrollPane wrapping a JTextArea is not a JTextArea, so instanceof JTextArea or reference equality checks return false. The Decorator and its enclosed component are not identical. Also, decorators can lead to many small objects that look alike, making maintenance harder if overused. If you need many objects sharing the same decoration state cheaply, consider sharing via Flyweight or applying the decoration to the class via inheritance.

14.2.3 Decorator Versus Composite — Same Diagram, Different Intent

Comparison — same boxes, opposite purpose. A side-by-side look at the class diagrams for decorator and composite shows the same shape: a shared abstraction Component, a leaf or concrete component, and a composite or decorator that holds a reference to the abstraction and inherits from it. Both use aggregation to a Component type while also implementing that type.

The difference is intent, not structure:

  • Composite composes objects. Adding a panel to another panel builds a tree of parts and wholes so a client can treat a single leaf and a whole hierarchy uniformly. The relationship is whole-part; operations are delegated to all children (for example, paint() paints self plus every child, getPrice() sums children).
  • Decorator enhances a single object. Adding scroll bars, colors, or behavior to a component enriches what that one component does. The relationship is wrapper-wrapped; operations are forwarded to the single wrapped object plus augmentation.

The companion comparison clarifies: Decorators add methods to particular instances, Adapters change the interface, and a Composite consisting of a single item is technically a Decorator in shape but not in intent. Once again, intent decides. Checking intent is essential when problems present similar hierarchies, and this contrast is a frequent exam discriminator.

Dimension Composite Decorator
Intent Build a tree; treat group and single object uniformly Add responsibility to a single object transparently
Cardinality One composite holds many children One decorator wraps one component (which may itself be a decorator)
Operation semantics Typically iterates over children (sum, paint all) Forwards to wrapped object then adds one embellishment
Example JPanel containing JButtons and nested JPanels JScrollPane(new JTextArea(...)), new SlashDecorator(new CoolDecorator(button))
When to pick Need part-whole hierarchy Need per-instance open-ended enhancement without subclass explosion

One-sentence rule: if the description says "compose objects into tree structures" choose Composite; if it says "add scrolling/border/color to any component and still use it as a component" choose Decorator.

14.2.4 Worked Examples

Example 1 — Text area with scroll bars through JScrollPane. Setup: a JTextArea is created as the concrete component. It implements the component abstraction that includes paint() and layout behavior. A JScrollPane is instantiated with the text area as its wrapped component via new JScrollPane(myTextArea). The pane implements the same component abstraction.

Execution trace when the UI validates and repaints:

  1. Container calls scrollPane.paint(g).
  2. JScrollPane.paint(g) calls wrapped.paint(g) — the text area renders its text into the viewport buffer.
  3. JScrollPane then paints vertical and horizontal scroll bars if content height/width exceeds viewport height/width, computes thumb position as thumbPos = scrollOffset * (viewportSize / contentSize), handles thumb movement, and clips content to the viewport rectangle.
  4. The frame adds the scroll pane via frame.add(scrollPane) exactly as if it were a plain component, because it still satisfies the component interface.

Result: text area code itself contains zero scroll logic. Answer highlighted: the scroll pane is usable wherever a Component is expected, demonstrating transparency. Sense-check: if you call getComponentCount() on the frame after adding, you count the scroll pane as one component — confirming wrapping preserves count.

Example 2 — Chaining multiple decorations. Start with a base button that renders a label "Dbutton". Wrap it first with a CoolDecorator that erases borders when mouse_over == false, then with a SlashDecorator that draws a red diagonal.

Code sketch from the companion: jp.add(new SlashDecorator(new CoolDecorator(new JButton("Dbutton"))));

Call sequence for paint(g) outward-inward-outward:

  1. SlashDecorator.paint(g) is invoked.
  2. It calls super.paint(g) which reaches CoolDecorator.paint(g).
  3. CoolDecorator calls wrapped.paint(g) — the JButton paints itself with standard L&F.
  4. If mouse_over == false, CoolDecorator draws Color.lightGray lines over the border to erase it.
  5. Return to SlashDecorator, which draws g.setColor(Color.red); g.drawLine(0,0,w1,h1);

The sequence paint()erase borderred slash shows that functionality accumulates without touching the original button class. If a new decoration such as a shadow is invented later, it is introduced as another Decorator subclass that wraps any Component, requiring no edit to existing component classes. Answer highlighted: N decorators produce N+1 layers, all responding as one Component, with behavior composed by ordered forwarding.

Example 3 — Non-visual decorator: stream buffering (supporting depth). Setup: raw FileInputStream reads bytes with one I/O per read(). Wrap it: InputStream in = new BufferedInputStream(new FileInputStream("data.txt")). Here FilterInputStream is the abstract Decorator, BufferedInputStream a concrete decorator. Each read() first checks an internal 8 KB buffer; only on miss does it delegate to the wrapped stream's read(). Chaining: new PushbackInputStream(new BufferedInputStream(new FileInputStream(...))) yields pushback + buffering. Takeaway: decorators are not limited to graphics; any object with pass-through methods can be enhanced per instance, and the same transparency — InputStream type preserved — applies.

Visual note on Example 2: if you screenshot before and after mouse hover, the left image shows a flat gray button with no border and a red slash, the right shows the same button with a raised border when mouse_over == true. The visual takeaway is that decoration is stateful — the same wrapped object looks different based on the decorator's mouse_over flag — while identity as JComponent never changes.

14.2.5 Student Questions and Answers

Q: Can we relate the decorator pattern to templates, the generic templates we studied in C++? A: Keep the two ideas separate — this is a terminology collision, not a conceptual one.

  • Decorator = design intent: enhancing the functionality of a component while its core behavior remains the same, and the decorated object still behaves as the component (is-a Component, contains a Component). The decision is about object composition at run time.
  • C++ generic templates = language mechanism: type parameterization at compile time, where vector<T> generates code for each T. The decision is about type abstraction, not about wrapping behavior.

Why it seems plausible: both use the word "template" loosely and both involve reuse. Why it is wrong: a C++ class template creates new classes; a Decorator creates new object layerings at run time without new classes per combination. Mixing them leads to answers that mention template <typename T> when the examiner asked about JScrollPane. Preferred mental model: if the problem statement says component objects need to be decorated visually, behaviorally, or with any added functionality, and usage after decoration must not change the main functionality, the answer is Decorator — never "use generics."

The house analogy reinforces this: a house remains a house after Diwali decorations; enhancements are added, the identity is not reduced. Learn patterns on their own terms and apply generic templates in their own context. Frequency: this confusion is called out explicitly in the lecture's second Q&A block; the professor repeats "keep separate" verbatim.

14.2.6 Professor Intuition and Analogies

The professor's Diwali-house image carries the entire abstract idea: enhancement without identity loss. Extend it: the electrical wiring is the paint() contract — unchanged after decoration; the decorative lights are added paintScrollBars() — visible only when the house (Component) is viewed. Where the analogy breaks — as already mapped — is that physical lights can be added without touching the walls, but a Decorator must intercept paint and forward; if forwarding is forgotten, decoration replaces rather than augments, which is a classic bug.

Another intuition offered is the usage test: check whether the object must remain usable exactly as before, with only added effects; if so, the structure of holding a reference and forwarding plus augmentation is the right mental model. Repetition in the lecture that enhancement should not change usage is an emphasis signal — exam questions often test whether the decorator's result can again be used as a component. A one-line answer template: "After decoration the object is still a Component; JScrollPane can be added to a Container where a JComponent was expected."

14.2.7 Industry Applications

Real-world: JScrollPane decorating JTextArea and other Swing components, JComponent as the component abstraction in Swing, and GUI toolkits where borders, scroll bars, and shadows are pluggable decorators. Beyond Swing, java.io.FilterInputStream family — BufferedInputStream, CheckedInputStream, DataInputStream, PushbackInputStream — is the textbook non-visual decorator chain. The broader practice is to keep component classes stable while providing a library of decorators for visual and behavioral enhancements, avoiding subclass explosion. For example, without Decorator, supporting 4 components times 5 decorations would require 20 subclasses; with Decorator it requires 4 + 5 classes and unlimited run-time combinations.

14.2.8 Exam Notes

Exam note: A problem description that lists many component types such as buttons, text boxes, labels, and says scroll bars, borders, or colors are needed for any of them without changing how the component is used points to Decorator. Stating that after decoration the object still behaves as a component and that behavior is implemented by calling the wrapped component's method and augmenting the result is expected in an answer. Distinguishing Decorator from Composite by intent is frequently tested even though their diagrams look alike — name the intent (enhancement vs composition), not just the structure. If you see "the class diagram looks like Figure 12.3" as a distractor, answer must add "but intent decides; here we need enhancement of a single object, so Decorator."

Recap + bridge — Decorator solves open-ended per-instance enhancement with transparency: wrap, forward, augment. It preserves usability as a Component while keeping the component closed for modification. Bridge to next pattern: Decorator changes what an existing object looks like or does without changing its interface. The next pattern, Command, does something orthogonal: it changes how a request is modeled — turning the request itself into an object that carries both what to do and whether it can be done.

14.3 Command Pattern — Turning Requests into Objects that Carry State and Behavior

14.3.1 Intent and Why Commands Are Objects

Hook — why does Paste know to stay grayed out until you Copy? The menu item is not gray because of the menu; the request knows it is not ready.

The command pattern treats a request as an object so that the request can carry both behavior — what to do — and state — whether it is enabled, what its icon is, and what its history is. In an object-oriented system, commands are not bare functions. They maintain state information together with the operation they perform. This is the decisive idea the lecture repeats: a Command is state plus behavior bundled as an object, not a callback.

A familiar illustration is an editor such as Microsoft Word with cut, copy, and paste. Paste is enabled only after cut or copy has placed something on the clipboard; otherwise it is disabled. The enable or disable state is not a property of the menu item alone but of the command's situation relative to other commands. Similarly, undo and redo operate over a history. The system maintains a list of previously executed commands so it can reverse a series of actions. Collecting commands as objects makes that history possible — a bare function pointer cannot remember which receiver and parameters were used last Tuesday.

Intuition — restaurant order slip. Think of a command like a restaurant order slip. The slip states what to cook (behavior — "two veg burgers, extra chai") and state about the order (table number, time, whether the kitchen is currently able to fulfill it — "out of Darjeeling tea, so this slip is disabled"). The waiter (Invoker) carries the slip to the kitchen (Receiver) without knowing how to cook. The slip can be queued, logged, or pinned to the board and undone by tearing it up. The mapping: slip = Command object, dish description = execute(), table/availability stamps = isEnabled() / putValue(NAME, SMALL_ICON), stack of slips = history list. Break point: a paper slip is static; a Command object can update its own enabled state after other commands execute, which paper cannot do without rewriting.

Scope — when Command applies. Assumption: you need to parameterize objects by an action, queue, log, or undo requests, and the request's availability depends on application state. When it holds: menus, toolbars, buttons where the same action appears in multiple places; undo/redo; transactional or delayed execution. When it fails: if the request is a simple one-shot call with no state, history, or multiple invokers, a direct method call or a single ActionListener inner class is simpler — Command would produce needless small classes.

Visual intuition: picture three layers left to right. Left: Invoker — JButton/JMenuItem with an Action. Middle: Command object with two compartments — top holds execute() arrow to the right, bottom holds a state dashboard with enabled: boolean, icon: Image, name: String. Right: Receiver — Document, Clipboard, Panel. The invoker's arrow points only to the command's execute(); the command's arrow points to the receiver's cut()/paste(). The takeaway: the invoker never sees the receiver; state sits on the slip, not on the button.

14.3.2 Structure, Participants, and Java Mapping

Formalize — participants. Participants are a command abstraction, concrete commands, the invoker, and the receiver.

  • Command — abstraction declaring execute() plus state methods. In Swing this is javax.swing.Action which extends ActionListener and adds isEnabled(), setEnabled(boolean), putValue(String, Object), getValue(String), removePropertyChangeListener.
  • ConcreteCommand — for example CutCommand, CopyCommand, PasteCommand, GreetingAction — implements the command interface to carry out a specific request, holds state needed for that request, and holds a reference to the Receiver that knows how to do the work.
  • Invoker — for example JButton, JMenuItem, toolbar button — holds a Command and calls execute() (or actionPerformed) when triggered, after checking isEnabled().
  • Receiver — the domain object that does the work — Clipboard, Document, Panel background.

Collaboration: client creates ConcreteCommand(receiver) and registers it with the Invoker via button.setAction(command) or button.addActionListener(command). When the user clicks, the invoker calls command.execute(); the command calls receiver.action(). Invoking the command means calling execute() on the command object, not on the receiver directly.

Variants in textbooks: the classic GoF Command has execute() and unDo(); Swing Action merges this with ActionListener.actionPerformed(ActionEvent). Both are the same idea — the interface that the invoker knows is uniform, the concrete behavior varies.

In Java Swing GUI programming, the same idea appears as the Action interface and ActionListener mechanism. Buttons and menu items are objects that wait for an action. An ActionListener is waiting for a particular action to happen; once a button is pressed, it becomes active and the action's behavior is executed. State methods such as isEnabled(), getEnabled(), setEnabled(), putValue(), getValue(), and removeProperty() let the framework query and change the enabled state, the icon, the name, and other properties of the action. There is an Action interface, an AbstractAction class that implements some of those methods, and a concrete class such as a greeting action that finally provides the specific behavior.

A demo example mentioned was a greeting command that produces Good Morning or Good Evening based on current timing, or a name-aware variant such as Good Morning, Mr. .... The pattern request is to learn how command execution is actually wired in Java — how execute() is triggered by the listener after the enabled check — by downloading and running small examples. The wiring is: AbstractAction stores a property map; JButton(Action a) copies a.getValue(NAME) and SMALL_ICON into the button model and registers an PropertyChangeListener so future a.putValue(...) updates the button automatically; on click, ButtonModel checks a.isEnabled() before firing actionPerformed.

Textbook note: an early simplification extends Button to also implement Command directly (class btnRedCommand extends Button implements Command), which eliminates the if-chain in actionPerformed, but the mature approach separates Command from Button via CommandHolder (setCommand/getCommand) so UI and action can vary independently — that separation is the true pattern.

Pitfalls — Command-specific traps.

  • State on the wrong object: placing enabled on the JButton instead of the Command. Then two buttons sharing Paste diverge — one enabled, one disabled — which should not happen; state belongs to the request.
  • Forgetting the enabled guard: calling execute() without isEnabled() check in the invoker, allowing Paste with empty clipboard and causing null or stale data.
  • Undo without storing enough: implementing unDo() but not saving receiver and parameters at execute() time, so undo cannot reverse because it does not know what changed.
  • Class proliferation panic: avoiding Command because it creates little classes. The companion notes that inner private methods are about as long as inner Command classes, so complexity is similar; anonymous inner classes reduce namespace clutter but still generate class files.

14.3.3 Worked Examples

Example 1 — Cut, copy, paste with enable logic. Setup: three command objects — CutCommand, CopyCommand, PasteCommand — each implements the command interface with execute() and state such as enabled, plus a shared Clipboard receiver object.

Initial state: PasteCommand.enabled = false (clipboard empty), button PasteButton constructed as new JButton(pasteCommand) so pasteButton.isEnabled() mirrors pasteCommand.isEnabled().

Trace 1 — user selects text "hello" and clicks Copy:

  1. copyButton receives actionPerformed, calls copyCommand.execute().
  2. CopyCommand.execute() calls clipboard.setContents("hello") and then pasteCommand.setEnabled(true) — firing a property change.
  3. Property listener updates PasteButton to enabled (ungrayed).
  4. CopyCommand also appends itself to history if undoable copy is needed.

Trace 2 — user clicks Paste at cursor position 5:

  1. Invoker checks pasteCommand.isEnabled() == true, so forwards to pasteCommand.execute().
  2. PasteCommand.execute() calls String s = clipboard.getContents()"hello" and document.insert(5, s).
  3. If the clipboard becomes empty after a cut-paste, PasteCommand may call setEnabled(false) again, graying the button.

The state and behavior travel together inside the command object. Answer highlighted: isEnabled belongs to PasteCommand, not to the button, and the history list stores the command with its clipboard snapshot, not a function pointer.

Example 2 — Undo history as a list of commands. Setup: each user action such as typing "Hi", formatting bold, or drawing a line is encapsulated as a command object with enough state to reverse itself. Classes: DrawCommand holds x, y, dx, dy, color, and a drawList: Vector<DrawData>; UndoCommand holds undoList: Vector<Command>.

Flow:

  1. User clicks Red to draw a line. redCommand.execute() does: drawList.add(new DrawData(x,y,dx,dy)); x+=dx; y+=dy; panel.repaint();
  2. The invoker's actionPerformed does in order: Command cmd = holder.getCommand(); undoCommand.add(cmd); cmd.execute(); — note the add excludes undoCommand itself to avoid undoing undo.
  3. Repeat 5 times, building drawList with 5 entries and undoList with 5 commands in execution order.
  4. User clicks Undo. undoCommand.execute() takes last = undoList.size()-1, fetches cmd = undoList.get(last), calls cmd.unDo() — which does drawList.remove(last); x = removed.getX(); repaint(); — then undoList.remove(last).

Redo would maintain a second list redoList and move commands between lists. Because commands are objects, the history preserves the receivers and parameters that were in effect at execution time. Answer highlighted: undoing 4 times removes lines in reverse execution order regardless of whether they were red or blue, because global undoList is chronological, not per-color. Sense-check: if undoList contained functions only, unDo would not know previous x to restore; storing DrawData solves it.

Example 3 — Java AbstractAction greeting. Setup: an AbstractAction subclass called GreetingAction holds String name and uses System.currentTimeMillis().

Code sketch:

class GreetingAction extends AbstractAction {
    GreetingAction(String name) {
        super(name);
        putValue(NAME, "Greet");
        putValue(SMALL_ICON, new ImageIcon("greet.png"));
    }
    void actionPerformed(ActionEvent e) {
        int hour = Calendar.getInstance().get(HOUR_OF_DAY);
        String msg = (hour < 12 ? "Good Morning" : "Good Evening")
                     + ", " + name;
        JOptionPane.showMessageDialog(null, msg);
    }
}
JButton btn = new JButton(new GreetingAction("Mr. Lee"));

Behavior: JButton is constructed with the action as its model — the button's isEnabled() appearance follows action.isEnabled(), and putValue(NAME, ...) updates the button label automatically. A button press calls actionPerformed. Changing the timing logic to prefer Good Morning, Ms. ... versus Mr. ... requires editing only GreetingAction, not the JButton code. A name-aware variant could branch on a sex attribute: if sex == female show Ms. else Mr. — mirroring the factory honorific example but now as command state. Answer highlighted: the AbstractAction stores presentation state (NAME, SMALL_ICON) so one command can back both a menu item and a toolbar button consistently.

14.3.4 Student Questions and Answers

Q: Is a command just a piece of behavior we can call? A: In object-oriented practice it is more. A command carries behavior that must be executed and state that describes whether it is currently usable, what it looks like, and what history it belongs to. That is why the command interface needs both an execution method (execute() or actionPerformed) and methods that manipulate state such as isEnabled() / setEnabled() and putValue() / getValue() for icon or name.

The state dimension explains two otherwise puzzling facts:

  1. Why Paste is gray: Paste.enabled is a function of other commands' execution (cut/copy put something on Clipboard), so enabled must live on the command graph, not on the widget.
  2. Why undo works: you need a list of past command objects, each remembering its own receiver and parameters (x,y, drawList). A bare function has no memory; an object does.

If an exam description mentions only behavior and never state, history, or enabled, the answer is likely not Command — it might be Strategy, which varies an algorithm, not a request. The lecture's discriminator sentence: "look for execute() together with isEnabled(), putValue(), icons, and a history for undo/redo."

14.3.5 Industry Applications

Real-world: editor commands cut, copy, paste, undo, redo in word processors, and any toolbar or menu where an item's enabled state follows application state. In Swing, javax.swing.Action, AbstractAction, ActionListener, and button models such as JButton realize the pattern for GUI commands — the same CutAction backs Ctrl+X, the Edit menu item, and the toolbar button, keeping isEnabled() consistent without triplicate code. Drawing programs where each stroke is a DrawCommand with unDo() that pops DrawData. The companion's Undo demo draws successive red or blue diagonal lines and undoes them in last-in-first-out order. Beyond GUIs, job-queue systems, macro recorders, and transactional systems use Command to queue, log, and replay requests.

14.3.6 Exam Notes

Exam note: A description that mentions execute() together with isEnabled(), setEnabled(), putValue(), icons, and a history for undo and redo points to Command, not Strategy or Decorator. The ability to collect commands and keep past history as a list is a distinctive signal — look for phrases such as "store additional information with the command" or "maintain a list of commands because you are undoing a series of actions." Discriminator drill: Strategy varies which algorithm is used (LayoutManager, Comparator); Command encapsulates a request with its readiness and history. If the phrase is "family of algorithms and the client supplies a strategy object at run time" that is Strategy; if it is "paste disabled until clipboard content and undo walks a list" that is Command.

Recap + bridge — Command reifies a request: behavior plus enabled state plus presentation plus rememberability. It decouples invoker from receiver and makes requests queueable and reversible. Bridge: while Command is about when and whether a request can be executed, the next pattern controls when and whether the object behind the request is even created or accessed — same interface, deferred cost.

14.4 Proxy Pattern — Controlling Access and Deferring Cost While Preserving the Client View

14.4.1 Intent and Problem Context

Hook — what if creating the object costs more than using it? Imagine a web page with fifty high-resolution images where the user will view only three — do you pay for all fifty up front?

A proxy is a person or object authorized to act on behalf of another. The core idea is that the client does not want to interact with the real object directly, or the real object is expensive to create, so the client works with a proxy. Only when the real need arises does the proxy bring the real object into play. The client gets the same feeling as if it were working with the real object. The pattern is described as magical because it changes creation or access behavior without changing the client's interface — the magic is that timing and cost disappear behind a familiar face.

Two classic motivations were emphasized. First, delaying instantiation. Creating an object immediately can be wasteful if it will not be used for a while. A proxy can wait for a particular amount of time, an action, or a focus event before creating or activating the real object. Second, deferring expensive resource use. Loading videos or high-resolution images for every element on a web page is costly, and many images will never be viewed. The design therefore loads an image only when the user reaches that location or clicks a tab. Other costs include remote connections. Calling remote methods through Remote Method Invocation eagerly would hold connections and resources; a proxy can defer that work until required. In all cases the modification must not affect the client or the real subject from the user's point of view — that transparency is the contract.

Intuition — two everyday proxies. The lecture anchors the idea with two images.

  • Proxy attendance: a classmate signs the attendance sheet on your behalf. The professor sees a signature through the same Attendance interface and treats it as if you were present; the real student is contacted only if needed.
  • Network proxy: a local gateway stands in for a remote server. Applications send requests to the gateway using the same fetch(resource) interface; the gateway defers the expensive network hop until a cache miss forces it.

Mapping: attendance sheet = Subject interface, classmate = Proxy, you = RealSubject, professor = client. The war-sleep image used while describing the modification — the proxy may wait or sleep before delegating — signals that proxies are about when and how the real work happens, not about changing what the work is. Break point: a real attendance proxy can forge a signature; a software Proxy cannot change the result — it must forward the same method with the same semantics, only adding timing, caching, or permission around it.

The companion text adds two more proxy intents that help you recognize the pattern in questions: access control — the proxy validates hasPermission(user) before forwarding — and copy-on-write — the proxy initially shares the original large object and only copies it when a mutation occurs. If the description says "check rights," "validate access," or "copy only when changed," that is also Proxy, not Decorator.

14.4.2 Structure, Participants, and Collaboration

Formalize — participants. The abstraction is a subject interface type. RealSubject provides the service specified by that interface. The proxy is useful when you want to modify the service to make it more versatile — for example to add lazy creation, caching, access control, or connection handling — while still offering the same services as the subject type.

Structure in words: define a Subject interface with operations such as paintIcon(Component, Graphics, x, y) or request(). RealSubject implements Subject with the heavy logic — load bitmap, open remote socket, check database. Define a Proxy class that also implements Subject, holds a reference real: Subject (aggregation), and knows how to locate or create it (filename, remoteURL, AccessController). The client interacts with the Proxy object as if it were the RealSubject. Each proxy method invokes the same method on the RealSubject with the necessary modification around it — for example waiting, sleeping, loading, or checking permission — and then returns the result.

The important constraints are that the proxy and the real subject share the same interface so client requests are implemented transparently, and that neither the client nor the real subject is affected by the modification except for the added versatility. Aggregation captures the relationship: the proxy aggregates the real subject; the client depends only on Subject.

Pseudocode for the core forwarding pattern:

interface Subject { void request(); }
class RealSubject implements Subject {
    RealSubject() { loadHeavyResource(); } // expensive
    void request() { // real work }
}
class Proxy implements Subject {
    RealSubject real; // null until needed
    String filename;
    Proxy(String f) { filename = f; }
    void request() {
        if (real == null) real = new RealSubject(filename); // lazy creation
        // or: if (!checkAccess()) throw...
        real.request(); // same interface, same result
    }
}
Subject s = new Proxy("elliott.jpg"); // client sees Subject
s.request(); // triggers creation only now

In the Java image-proxy idiom, Proxy extends JPanel and the shared interface is paint(Graphics) / paintIcon. The proxy creates a MediaTracker to monitor loading, spawns a monitor thread, and on first paint either draws a placeholder rectangle or the loaded image — the client JPanel code that adds image to the layout never branches on proxy versus real.

Scope — when Proxy applies. Assumption: the extra behavior is about access or creation — lazy loading, caching, permission, remote connection, copy-on-write — and the client should not know or care. When it holds: initialization is expensive and many objects may never be used; remote objects; access control. When it fails: if you are adding visual embellishment that should accumulate openly (use Decorator), or translating to a different interface (use Adapter), or varying among interchangeable algorithms (use Strategy). Proxy never changes the interface; Adapter does. Proxy controls whether and when the real object is reached; Decorator adds what is visible after reaching it.

Visual intuition: draw a timeline left to right. At time zero, the client holds a tiny hollow rectangle labeled Proxy with a dotted line to a large solid rectangle labeled RealSubject that is still dashed — not created. First paintIcon call at t=1: the proxy rectangle shows a thin black outline with text "loading..." and spawns a thread arrow downward to the real rectangle that materializes gradually from dashed to solid. At t=2, the hollow rectangle now contains the solid image. The x-axis is time, y-axis is memory cost. The shape is a step function: cost stays near zero until first access, then jumps to real cost. Takeaway: Proxy flattens expensive startup cost and shifts it to first use, which matters when only a minority of objects are ever touched.

14.4.3 Worked Examples

Example 1 — Image proxy with lazy paintIcon. Setup: a label component is to display an image when the user reaches it, not before. Instead of creating a heavyweight real image object at startup, create an ImageProxy that implements the same Icon or Subject interface as the real image and initially holds no bitmap data — only filename = "elliott.jpg", width=321, height=271, tracker, and real == null.

Execution trace mirroring the companion code:

  1. Startup: ImageProxy image = new ImageProxy("elliott.jpg", 321, 271); p.add("Center", image); — constructor creates img = Toolkit.getDefaultToolkit().getImage(filename), adds to MediaTracker, starts imageCheck thread with tracker.waitForID(0,1) minimal wait.
  2. First paint(g) — called by Swing before image bytes arrive: tracker.checkID(0) == false, so g.drawRect(1,1,width-2,height-2) draws placeholder border. Cost: one rectangle, near zero.
  3. Monitor thread sleeps 1000 ms polling tracker.checkID(0). For the demo the sleep is artificially long to make the delay visible.
  4. When check returns true, thread calls repaint().
  5. Second paint(g) — now tracker.checkID(0) == true: proxy does width = img.getWidth(frame); height = img.getHeight(frame); g.fillRect(0,0,width,height); g.drawImage(img,0,0,this); and caches real so subsequent paints skip loading.

The label code is unchanged — it simply calls paintIcon() on what it believes is an image. User perception is that the image appeared when needed, but initialization cost was deferred until first use. The proxy's paintIcon implementation illustrates the combine-and-delegate rule: add loading behavior, then invoke the real subject's method. Answer highlighted: placeholder on first paint, real image on second, client code identical for both.

Example 2 — Tab-based deferred creation. Setup: an interface contains several tabs — Overview, Charts, Images — each containing heavyweight charts or images. Creating all real subjects at startup would consume memory and startup time proportional to tab count.

Design: each tab's content panel holds Subject content = new ChartProxy(datasetId) instead of new RealChart(dataset). The ChartProxy stores datasetId and real == null.

Trace:

  • At startup, three proxies are created, zero charts loaded. Memory cost: proxies only.
  • User clicks "Charts" tab. Tab framework calls content.paint(g). ChartProxy.paint sees real == null, so runs real = new RealChart(loadDataset(datasetId)) — the expensive parse and layout now happens.
  • real.paint(g) renders the chart.
  • User switches away and back. Second paint finds real != null and reuses the cached object — no reload.

The client that manages tab selection interacts only with the Subject interface; the word "Proxy" appears only in variable type during wiring. Answer highlighted: deferred creation until tab click, with caching after first use, keeping startup time flat regardless of tab count.

Example 3 — Remote method invocation proxy. Setup: a service object lives on a remote host — for example a credit authorization service. Directly establishing a connection for every possible call at startup is expensive and holds scarce remote connections.

Local Proxy implements the same service interface as the remote RealSubject, say ICreditAuthorizationService.requestApproval(CreditPayment, terminalID, merchantID). The proxy holds a locator remoteRef but does not connect until a method is actually invoked.

Trace on proxy.requestApproval(payment, tid, mid):

  1. If no socket exists, proxy opens a socket to the remote host, marshals arguments, sends via stub, waits for reply.
  2. On response, unmarshals result and returns it as if the client had called the remote object directly.
  3. On subsequent calls, the proxy may reuse the connection or apply caching.

The delay and connection logic are hidden behind the shared interface. The companion also notes that EJB containers use a proxy to the connection pool: when all connections are busy, callers receive a proxy that becomes the real connection when one is free — same interface, delayed binding. Answer highlighted: remote cost hidden; client code servicesFactory.getCreditAuth().requestApproval(...) cannot tell local from remote.

14.4.4 Professor Intuition

Two everyday images anchor the idea: making a proxy attendance entry for a class, and using a network proxy to reach a remote resource. Both involve an authorized stand-in that acts so the requester does not need to reach the real entity immediately. The war-sleep image used while describing the modification — the proxy may wait or sleep before delegating — signals that proxies are about when and how the real work happens, not about changing what the work is. A useful self-check the professor implies: ask "if I removed the proxy, would the observable result change?" If the answer is no — only timing or cost changes — it is Proxy. If the result should look different — border added, color changed — it is Decorator.

14.4.5 Industry Applications

Real-world: lazy image and video loading on web pages — IntersectionObserver patterns and placeholder <img> that loads only when scrolled into view; tab content that loads only on click; Java Remote Method Invocation where a generated stub is the proxy; EJB connection-pool proxies; copy-on-write for large document objects where a second instance shares storage until modified. Any scenario where initialization is expensive and many objects may never be viewed benefits from a proxy that presents a lightweight placeholder icon backed by on-first-paint loading. The companion also mentions enterprise connection proxies where the proxy validates access permissions before forwarding — a protection proxy.

14.4.6 Exam Notes

Exam note: Phrases such as "delay instantiation," "expensive to load," "no need to load an image the user does not look at," "loading until the user clicks the tab," and "the client should not be affected" signal Proxy rather than Decorator or Adapter. A question that says "the interface of the proxy and the real subject must be the same, and the proxy holds a reference to the real subject and invokes the same method with modifications, is describing Proxy's structural constraints. Contrast drill: Proxy keeps same interface and controls access timing; Decorator keeps same interface and adds visual/behavioral responsibility; Adapter changes the interface. If the phrase is "authorized to act on behalf" or "wait/sleep before delegating," eliminate Decorator immediately.

Recap + bridge — Proxy preserves the client's view while optimizing when the real object is created or whether it is accessed at all — virtual, remote, protection, or copy-on-write. Bridge to next pattern: Proxy controls access to one object behind the same interface. Strategy controls which algorithm is used among a family of interchangeable algorithms behind the same interface. The question shifts from "when to materialize?" to "which computation to run?"

14.5 Strategy Pattern — Encapsulating Interchangeable Algorithms

14.5.1 Intent and Problem Context

Hook — why should a Container know how to do GridLayout and BorderLayout and every future layout you will invent next year?

Strategy captures the idea that there can be different strategies — related algorithms — to solve the same problem, and the system should be able to change the algorithm that is in use. Each algorithm is placed in its own class so variants are isolated, and the client can choose among them without modifying the interface through which the work is requested. The pattern is about algorithms and their variation. Whenever an algorithm needs to be executed and we wish to vary its concrete steps while keeping the surrounding context stable, Strategy applies.

Common drivers include families such as encryption algorithms, compression algorithms, layout algorithms, file storage formats, and sorting orders. The need to add a new algorithm without editing existing clients, and the need for clients to supply custom versions that replace a standard algorithm, both point to this pattern. The lecture explicitly calls Strategy an algorithm-family pattern: you can "create objects without making a change in the interface" and "clients want to replace the standard algorithm with custom versions."

Intuition — navigation apps. Think of a navigation app as the Context and routing algorithms as Strategies. The destination entry screen is Container; the route computation could be FastestStrategy, ShortestStrategy, or EcoStrategy. Each strategy implements the same computeRoute(origin, destination) but optimizes a different criterion. You select one at run time via setStrategy(new FastestStrategy()); the map screen then calls strategy.computeRoute(...) without knowing which formula is inside. The mapping: navigation engine = Context (Container), route algorithm = Strategy (LayoutManager), algorithm choice = setLayout(new GridLayout(...)). Break point: navigation strategies may share heavy map data, but Strategy objects should stay loosely coupled — the Context passes only what the algorithm needs, not the entire map database, to avoid leaking state.

The companion motivation lists five canonical Strategy situations: saving files in different formats, compressing with different algorithms, capturing video with different codecs, breaking lines with different strategies, and plotting the same data as line graph versus bar chart. All share the same form: one job, many ways to do it, client should choose.

Scope — when Strategy applies. Assumption: the algorithms are related — they solve the same problem with the same input-output contract — but differ in steps or criteria. When it holds: you expect new algorithms over time and want the Context closed for modification. When it fails: if there is only one algorithm and no foreseen variation, Strategy adds indirection; if algorithms need very different interfaces or share no common contract, they are not a family — consider separate abstractions instead.

14.5.2 Structure, Participants, and Collaboration

Formalize — participants. Participants are a strategy abstraction, concrete strategies, and a context that uses the strategy.

  • Strategy — interface that declares the algorithm operation, spoken of as doWork() or a more specific name such as layoutContainer(Container parent) or compare(Object o1, Object o2) or getTotal(Sale).
  • ConcreteStrategy — each variant, for example BorderLayout, GridLayout, BoxLayout, CompareByName, PdfSave — implements Strategy with one variant of the algorithm.
  • Context — for example a Container, a Collection sorter, a Sale, or a File — holds a reference strategy: Strategy and delegates the algorithm call to it. Clients supply strategy objects, often at run time, and can switch the strategy whenever needed.

Collaboration in words: the context receives a request to perform its work — container.layout(), sale.getTotal(), file.save(). Instead of implementing the algorithm internally or branching with if (type == "grid") ... else if (type == "border"), it calls the appropriate method on the currently held strategy object: strategy.layoutContainer(this) or strategy.compare(a,b) or strategy.save(file). The concrete strategy executes its variant and returns the result. Because all strategies share the same interface, the context never needs to know which variant is in use. UML note: the association arrow goes from Context to Strategy interface, not to a concrete class, so any implementation can be bound.

Pseudocode:

interface Strategy { void doWork(Context c); }
class Context {
    Strategy strategy;
    void setStrategy(Strategy s) { strategy = s; }
    void request() { strategy.doWork(this); } // delegation, no if
}
Strategy s = new GridLayoutStrategy();
Context ctx = new Panel();
ctx.setStrategy(s);
ctx.request();

The companion Context example Context has setBarPlot() / setLinePlot() that assign plotStrategy = new BarPlotStrategy() versus LinePlotStrategy, and plot() { plotStrategy.plot(x,y); } — identical shape, different domain.

Visual intuition: picture the Context as a universal remote with one slot labeled Strategy. The x-axis is time or user choice, y-axis is algorithm cost or quality. At any moment the slot holds one cartridge — BorderLayout cartridge shows five zones, GridLayout shows uniform cells, BoxLayout shows a single column. The takeaway: the remote's buttons (layoutContainer) never change; only the cartridge does, so the interface is stable while behavior varies.

Pitfalls — Strategy-specific traps.

  • Conditional instead of polymorphism: keeping a switch(layoutType) inside Container instead of delegating. The whole point is to eliminate the conditional.
  • Strategy with divergent interfaces: trying to force unrelated algorithms into one doWork() that needs wildly different parameters, leading to "parameter object with unused fields." Keep the interface broad enough to cover the family, but if signatures diverge completely, split families.
  • Leaking Context state: passing the entire Context when only width, height is needed, increasing coupling. Prefer passing minimal data or having the Strategy query only what it needs.
  • Confusing Strategy with State: both delegate, but Strategy is client-chosen and one-at-a-time; State is internally driven with transitions among possibly many active states. Interviewers test this distinction.

14.5.3 Worked Examples

Example 1 — Layout managers as strategies for a container. Setup: a Container such as a JPanel holds many components — buttons, text boxes, labels — for example the 10 to 20 keys of a mobile phone screen or the cells of a calculator or calendar grid. Each component must be placed according to a layout rule.

Participants: Strategy = LayoutManager with method layoutContainer(Container parent). ConcreteStrategies = BorderLayout (places North/South/East/West/Center), GridLayout(rows, cols) (uniform grid), BoxLayout (single row or column with glue), plus any custom layout a client defines like CircularLayout. Context = Container (JPanel).

Trace:

  1. At construction or run time, client calls container.setLayout(new GridLayout(4,3)) — injecting the strategy. To switch, call container.setLayout(new BorderLayout()) — no edit to Container source.
  2. When the UI validates via container.validate() or container.doLayout(), the container executes layoutManager.layoutContainer(this).
  3. GridLayout.layoutContainer(parent) iterates children and assigns bounds x = col * cellW, y = row * cellH, w=cellW, h=cellH.
  4. BorderLayout.layoutContainer(parent) assigns north to top strip y=0, h=preferredH, center to remaining rectangle.

Adding a new layout — for example a circular or waterfall arrangement — means writing a new class that implements LayoutManager without editing Container. The default layouts are standard, but the pattern explicitly allows clients to replace them with custom versions while the container interface stays unchanged. This is why every Swing GUI program necessarily uses layout managers; no GUI program exists without them. Answer highlighted: context is Container, strategy is LayoutManager, concrete strategies are BorderLayout, GridLayout, BoxLayout, algorithm method is layoutContainer — that four-part mapping is the exam answer template.

Exam note from the lecture: layout managers were highlighted as the most popular example of Strategy and are a frequent exam mapping; the answer must label each participant correctly.

Example 2 — Sorting with Comparator as strategy. Setup: a collection of records has fields such as id, name, age, and city. The sorting algorithm itself — say quicksort in Collections.sort — is fixed, but the comparison rule varies.

Participants: Strategy = Comparator with method compare(a,b) -> int. ConcreteStrategies = CompareById implementing return a.id - b.id, CompareByName implementing return a.name.compareTo(b.name), CompareByAge, etc. Context = the collection utility that sorts, such as Collections.sort(list, comparator) or the List itself.

Trace with concrete numbers: records [ (id=3, name="Zara", age=22), (id=1, name="Amit", age=34), (id=2, name="Li", age=22) ].

  • With CompareById, compare sorts by id: result [1,2,3].
  • Swap strategy to CompareByName: compare uses String.compareTo, result [Amit, Li, Zara].
  • Swap to CompareByAge then CompareByName tie-breaker: comparator could chain — first compare age, on equality compare name.

The main difference between the variants is the comparison criterion, which is exactly what the strategy encapsulates. The same sort routine produces orders by name, by age, or by city without any change to the sorting code. *Answer highlighted: Strategy encapsulates the criterion, not the sort loop; changing comparator changes order while sort() code is untouched.*

Example 3 — File storage and compression or encryption choices. Setup: an application must save a file in different formats — PDF, word processor format, plain text — or compress it with different algorithms or encrypt it with different ciphers.

Participants: Strategy = SaveStrategy or CompressionStrategy with an operation such as save(File) or compress(byte[] data) or NextGen's ISalePricingStrategy.getTotal(Sale). ConcreteStrategies = PdfSave, WordSave, ZipCompression, RarCompression, AesEncryption, and companion pricing examples PercentDiscountPricingStrategy (10 percent off) versus AbsoluteDiscountOverThresholdPricingStrategy (50 dollars off if total exceeds 500 dollars). Context = a File or Document or Sale object whose save() or getTotal() method delegates to the current strategy.

Trace: Sale s = new Sale(); s.setPricingStrategy(new PercentDiscountPricingStrategy(0.10)); Money total = s.getTotal(); // s delegates to strategy.getTotal(this) returns subtotal * (1-percentage). Client later chooses file.save(new WordSave()) at run time without editing File. Hundreds of similar choices — image format, character encoding, export type — follow the same delegation form. Answer highlighted: one Context method, many interchangeable algorithm objects, run-time swap with no caller change.

Visual note on Example 1: sketch three panels side-by-side, each with the same six buttons labeled 1–6. Left with GridLayout shows two rows of three equal cells. Middle with BorderLayout shows 5 labeled zones with button 1 at North stretching full width. Right with BoxLayout shows a vertical stack centered. The takeaway: identical children, three computations of x,y,w,h, one stable Container interface.

14.5.4 Industry Applications

Real-world: Swing layout managers BorderLayout, GridLayout, BoxLayout as strategies for Container; collection framework comparators Comparator and Comparable as sorting strategies; file exporters, serializers, and archivers that vary format and algorithm; encryption and compression libraries where algorithm families are pluggable. In NextGen, pricing policies PercentDiscountPricingStrategy and AbsoluteDiscountOverThresholdPricingStrategy are textbook Strategy instances — each implements ISalePricingStrategy.getTotal(Sale) and the Sale delegates return pricingStrategy.getTotal(this). Any framework that allows clients to replace a standard algorithm with a custom implementation through a common interface without changing the caller's code is Strategy in action.

14.5.5 Exam Notes

Exam note: If a problem says you have a family of related algorithms, each algorithm in a separate class, the client should supply a strategy object and the context should call the strategy's algorithm method (layoutContainer, compare, getTotal, doWork), that is Strategy. Discriminators:

  • Command encapsulates a request with state and history (execute, isEnabled, undo list).
  • Decorator adds responsibility around a component while preserving its interface (JScrollPane wrapping JTextArea).
  • Strategy encapsulates an interchangeable algorithm so the context can vary which computation it uses (Container + LayoutManager).

Expect a question that asks to identify Strategy versus Proxy or Factory — the identifying phrase for Strategy is "family of algorithms, each in its own class, client supplies at run time, context delegates" and often "clients want to replace the standard algorithm with custom versions." Naming the four participants correctly is scored.

Recap + bridge — Strategy isolates algorithmic variation behind a common interface so Context stays closed while algorithms remain open and pluggable. It replaces conditionals with polymorphism. Bridge: Strategy varies which algorithm is used. Factory varies which object is created and hides the branching that decides — and, as you will see, the two patterns often collaborate: a Factory creates the Strategy the Context will use.

14.6 Factory Patterns, Factory Method, and Iterator as a Factory Method

14.6.1 Limitations of Constructors and the Factory Idea

Hook — a constructor says "make a Circle." What if you need to say "make the right Shape for this text description" and you will not know which one until run time?

Knowledge of constructors asks first what a constructor does at least: it constructs an object and it has the same name as the class. Limitations follow from that form. A constructor is typically a public method that exposes how objects are created, ties creation to a single concrete class, cannot easily hide complex creation logic, cannot easily return a subtype chosen by parameters or cache results, and cannot easily vary the product family without editing client code. The talk listed three to four creational patterns that address these limits; factory and factory method are among the most popular, even though factory in its simplest form is not a classic Gang of Four member.

The factory idea is to create objects without exposing the instantiation logic to the client. The client interacts with an interface and passes parameters. The factory decides which concrete class to instantiate. Newly created objects are referred to through a common interface or common parent that declares shared methods; specific implementations differ. The factory is a pure fabrication object whose responsibility is to handle creation for a hierarchy of products. The underlying motive is separation of concerns and cohesion: when creation involves special considerations, complex branching, or choices about which subtype to return, that logic is moved into a cohesive helper object rather than spreading it across clients. That helper can also introduce performance management and resource control such as caching or recycling — the factory can return a cached instance instead of creating a new one without any client change.

Formalize — constructor versus factory. Constructor: new ConcreteClass(args) — caller names the concrete class, caller owns new, return type is exactly ConcreteClass, logic is in caller. Factory: factory.create(args): ProductInterface — caller names desired product characteristics, factory owns new and branching, return type is interface, concrete choice hidden. The cost of new across many clients is duplicated if-branch logic and scattered import dependencies; Factory centralizes it, achieving low coupling to concrete products and high cohesion of creation concern. GRASP mapping: Factory is often a Pure Fabrication; creation also involves Creator and Information Expert, but Factory replaces them when cohesion would suffer.

Scope — when Factory helps. Assumption: creation is non-trivial — conditional on data, needs caching, needs to hide a product family, or clients otherwise would couple to many concrete classes. When it holds: choosing among Circle/Rectangle/Square by a string, choosing LastFirst versus FirstFirst by comma, or choosing an iterator type per collection. When it fails: if construction is trivial and stable — one concrete class, no variation — a factory adds indirection for no protected variation. A simple new is then clearer and faster.

14.6.2 Simple Factory — Parameter-Driven Creation

Formalize — Simple Factory structure. A Simple Factory hides an if or switch chain that selects the concrete product. One factory class, one method like getShape(String type) or getNamer(String entry), many product types sharing a common parent/interface. The decision is inside the factory method body, based on the parameter.

Example 1 — Title factory for Mr. versus Ms. based on data. Setup: display logic needs an honorific before a name. If the data attribute sex is male, the prefix is Mr.; if female, Ms. plus Name. Instead of letting every client write that conditional, a factory method such as createPerson(sex, name) encapsulates it.

Trace with data:

  • Input: sex="male", name="Ashok Kumar" → factory branch if (sex.equals("male")) return new MalePerson(name) → object whose getTitle() returns "Mr. Ashok Kumar".
  • Input: sex="female", name="Priya Singh"return new FemalePerson(name)getTitle() returns "Ms. Priya Singh".
  • Client code: Person p = factory.createPerson(sex, name); label.setText(p.getTitle()); — no if in client.

All returned classes share a common parent Person and common methods getTitle(); the distinction is which concrete class is created. The creation call just supplies the parameter that describes what is wanted — "this is what I want" — and the factory handles the rest. Real-world: any branching that chooses among several possible classes depending on data provided to it is a factory situation. Adding a new title, for example Dr., adds one concrete class and one branch in the factory, not edits in every client.

Example 2 — Name ordering factory for international variation. Setup: some cultures write first name followed by last name, others write last name first, often signaled by whether the name string contains a comma. The hierarchy contains a LastFirst name type and a FirstLast name type that share a common name abstraction Namer with methods getFirst() and getLast().

Companion code:

class NamerFactory {
  public Namer getNamer(String entry) {
    int i = entry.indexOf(",");
    if (i > 0) return new LastFirst(entry);
    else        return new FirstFirst(entry);
  }
}
class LastFirst extends Namer {
  LastFirst(String s){
    int i=s.indexOf(",");
    if(i>0){ last=s.substring(0,i).trim(); first=s.substring(i+1).trim();}
    else { last=s; first=""; }
  }
}
class FirstFirst extends Namer {
  FirstFirst(String s){
    int i=s.lastIndexOf(" ");
    if(i>0){ first=s.substring(0,i).trim(); last=s.substring(i+1).trim();}
    else { first=""; last=s; }
  }
}

Trace:

  • Input "John Doe" → no comma → new FirstFirst("John Doe")i=4first="John", last="Doe", getText() might return "John Doe".
  • Input "Doe, John" → comma at i=3 → new LastFirst("Doe, John")last="Doe", first="John", getText() returns locale-aware form.

The client does not decide the concrete class; it supplies the data and receives an object through the common interface. Special creation needs are handled inside the factory with whatever complex logic is appropriate. Answer highlighted: comma presence drives the factory branch, and the client sees only Namer. Sense-check: calling new LastFirst("Doe, John").getFirst()"John" confirms parsing direction.

Example 3 — Shape factory. Setup: a program works with shapes Circle, Rectangle, and Square that share a Shape interface with a method such as draw().

Companion-style implementation:

interface Shape { void draw(); }
class ShapeFactory {
  Shape getShape(String type){
    if(type.equalsIgnoreCase("circle")) return new Circle();
    if(type.equalsIgnoreCase("rectangle")) return new Rectangle();
    if(type.equalsIgnoreCase("square")) return new Square();
    return null;
  }
}

Trace on client:

  1. Input "circle"factory.getShape("circle") returns new Circle() typed as Shape.
  2. Client calls shape.draw() — polymorphic, no instanceof.
  3. Input "rectangle" returns new Rectangle(); adding Triangle later adds one branch.

Instead of letting the client write new Circle() or new Rectangle() everywhere, ShapeFactory centralizes the decision. The user interacts only with ShapeFactory and the Shape interface; the decision about which concrete class to instantiate is hidden. Introducing a new shape requires adding a new concrete class and updating factory logic, but client call sites that depend only on Shape remain unchanged. Introducing caching — for example reusing an expensive shape or image — is also hidden inside the factory, which aligns with the note that performance and management such as caching can be added behind the factory interface.

Visual intuition for Simple Factory: diagram shows a funnel. Top is many client arrows each labeled with a parameter string ("circle", "Doe, John", "male"). Middle is the Factory diamond with an if/switch inside. Bottom is a set of concrete product boxes all plugged into one Product socket. The takeaway: many inputs, one decision point, one stable interface outward.

14.6.3 Factory Method — Definition and Comparison with Constructor

Formalize — Factory Method is inheritance + polymorphism for creation. Factory method is usually compared directly with a constructor. Where a constructor exposes the creation path through a public method named after the class, a factory method defines an interface for creating an object but lets subclasses decide which class to instantiate.

Structure: declare abstract Product factoryMethod() in a Creator抽象. ConcreteCreatorA overrides to return new ConcreteProductA(); ConcreteCreatorB to return new ConcreteProductB(). The client uses Creator c = new ConcreteCreatorA(); Product p = c.factoryMethod(); — client depends only on Creator and Product.

The classic illustration recommended is iterator. An iterator lets you traverse any collection without learning its internal representation. Whether the collection is a singly linked list, a doubly linked list, a priority queue, a stack, or a queue, you work with the iterator abstraction that offers operations such as hasNext() and next(). You traverse from the outside; you do not enter the collection's private structure.

That traversing capability is a factory method situation: whenever the term factory appears, it usually means an operation that creates objects, and iterator is a creation operation that produces an appropriate iterator object for the particular collection. The collection hierarchy therefore does not expose how iteration is implemented per concrete collection; each concrete collection supplies its own iterator creation method and returns an object through the common Iterator interface. That method is the factory method. The companion swim-meet example mirrors this: abstract Event declares abstract Seeding getSeeding(); and PrelimEvent.getSeeding() returns new CircleSeeding(...) while TimedFinalEvent.getSeeding() returns new StraightSeeding(...) — two creator hierarchies driving two product hierarchies.

Comparison — Simple Factory versus Factory Method.

Aspect Simple Factory Factory Method
Decision One class with if/switch on a parameter Polymorphism: each subclass decides
Knowledge Factory knows all products Base Creator knows only Product interface
Extension Add product → edit factory if Add product → add ConcreteCreator subclass, no edit to existing creators
GoF status Not GoF; often called Concrete Factory, idiom GoF pattern
Example ShapeFactory.getShape(type) collection.iterator()LinkedList.iterator() returns ListIterator, PriorityQueue.iterator() returns heap iterator

One-sentence rule: if creation is "choose by data value," think Simple Factory; if creation is "let my subclass choose," think Factory Method. Both hide instantiation logic from the client and refer to products through a common interface.

Pitfalls — Factory traps.

  • Factory with one product: writing a helper that always creates exactly one XMLReader and calling it a factory method. A single-product helper is not a Factory Method — there is no product family and no hierarchy where subclasses decide. The lecture flags this exact trap.
  • Constructor still coupled: calling new Circle() in many clients despite having a factory; the factory must actually be used at call sites to get the benefit.
  • Leaking concrete types: declaring the variable as Circle c = factory.getShape("circle") defeats substitution — keep it as Shape.

14.6.4 Worked Examples Continued — Iterator as Factory Method

Example 4 — Iterating heterogeneous collections. Setup: three collections hold the same logical elements — a LinkedList as a singly linked chain, a DoublyLinkedList, and a PriorityQueue ordered by priority. Client code that wants to process every element writes the same loop regardless of structure:

Iterator it = collection.iterator(); // factory method
while (it.hasNext()) {
    Element e = it.next();
    process(e);
}

Trace per concrete collection:

  • LinkedList.iterator() returns new LinkedListIterator(head) that walks node = node.next.
  • DoublyLinkedList.iterator() returns its own iterator walking next forward and potentially prev for backward variants.
  • PriorityQueue.iterator() returns new HeapIterator(heapArray) that walks heap order (not insertion order), using heap index arithmetic left=2*i+1.

The client never sees those details — no if (collection instanceof LinkedList) — because the creation method is the factory method: it manufactures the right iterator type for the underlying collection. This is described as a magical technique for implementation because it unifies traversal across unrelated structures with one stable interface.

Second illustration from the companion: PrelimEvent.getSeeding()CircleSeeding versus TimedFinalEvent.getSeeding()StraightSeeding. Client code Seeding s = event.getSeeding(); Enumeration e = s.getSwimmers(); works identically for both events. Answer highlighted: the factory method is iterator() / getSeeding(), the Creator hierarchy is Collection / Event, the Product hierarchy is Iterator / Seeding, and the creation is deferred to subclasses. Sense-check: adding a new collection Stack with StackIterator requires adding Stack and its iterator only — no edit to existing LinkedList or PriorityQueue — proving Open-Closed for creation.

Visual intuition: draw two parallel hierarchies. Left hierarchy is Creator: Collection at top with abstract iterator(), children LinkedList, DoublyLinkedList, PriorityQueue each with concrete iterator() arrow. Right hierarchy is Product: Iterator at top with hasNext/next, children ListIterator, HeapIterator. A dashed arrow from each creator child points to its product child, labeled "creates." The x-axis is creation timing, y-axis is abstraction level. Takeaway: each creator subclass knows which product subclass matches its internal representation, and the client sees only the two abstractions.

14.6.5 Student Questions and Answers

Q: What is the difference between a factory method and a plain constructor, and can you illustrate with iterator? A: A constructor is tied to one class, has the same name as that class, and when it is public it exposes how objects are made to every client — caller must write new ConcreteClass(...) and know the class. A factory method hides that path. You call a method declared in an abstraction — for example createIterator() or iterator() or getSeeding() — and the concrete collection or event decides which iterator or seeding class to create. The returned object is referenced through a common interface Iterator with operations such as hasNext() and next(), or Seeding with getSwimmers(). You therefore traverse from the outside without entering the collection's private structure, and the same client loop works for a singly linked list, a doubly linked list, a stack, or a priority queue because each concrete collection supplies its own factory method implementation. The full answer template for exams: state constructor limitation (public, same-name, exposes creation), state factory method remedy (interface + subclass decision), give iterator hasNext example, and stress client code unchanged.

14.6.6 Industry Applications

Real-world: ShapeFactory that returns Circle, Rectangle, Square through a Shape interface; name factories that format names by locale or comma convention; honorific factories that produce Mr. versus Ms. objects; Swing creation helpers; any library where getInstance or create... hides branching or caching. The canonical platform example is Java collections and their iterators — List.iterator(), Set.iterator(), Queue.iterator() are the textbook factory method, and Java's Calendar.getInstance() / Toolkit.getImage() show simple factories. In NextGen POS, ServicesFactory and PricingStrategyFactory are singleton factories that read the concrete adapter or strategy class name from a system property and instantiate via reflection Class.forName(className).newInstance() — hiding concrete choice completely behind an interface and enabling zero-code swap.

14.6.7 Exam Notes

Exam note: A question that shows a constructor and asks its limitations should mention three points: it is public and exposes the way objects are created, it has the same name as the class and ties the caller to a concrete class, and it cannot easily hide complex branching, caching, or family choice. Then state the remedy: a factory hides the instantiation logic and refers to products through a common interface, letting a parameter or subclass decide the concrete type. Trap: an examiner may show a code fragment that merely creates a single XMLReader instance and ask whether it is a factory method — it is not, because it does not define a hierarchy that follows the factory method structure and it just creates one instance; comparison with iterator helps: iterator's hierarchy defines a factory method at the collection abstraction that each concrete collection implements. The discriminator is product family + hierarchy versus single instance helper. Creating a single object through one helper method without a product family is not a true factory method.

Recap + bridge — Simple Factory centralizes a parameter-driven if so clients depend on an interface; Factory Method pushes the decision into a hierarchy so subclasses decide which product family member to create. Iterator is the canonical Factory Method because each collection knows its own iterator. Bridge: these creation patterns solve how objects come into existence. The next layer of reuse sits above creation: whole sets of cooperating creators and products — toolkits versus frameworks — where factories and other patterns appear as extension points.

14.7 Toolkits, Templates, Frameworks, and How Patterns Fit Reuse

14.7.1 Toolkits Versus Frameworks Versus Patterns

Hook — a toolkit gives you bricks, a framework gives you a half-built house. Which one calls which?

Patterns, toolkits, templates, and frameworks all support reuse but at different levels and with opposite control flow. Keeping them distinct is a recurring exam requirement.

Toolkits are a collection of related and reusable classes, for example the C++ standard library or Java's java.util package with ArrayList, HashMap, Collections. They are called by the application; the application controls the flow and uses toolkit classes as needed — the application is the caller, the toolkit the callee. Toolkit classes are largely independent; you can use ArrayList without HashMap.

Templates in C++ are language-level generic mechanisms — template<typename T> class vector<T> — that generate type-safe classes or functions at compile time. The lecture explicitly warned not to confuse generic templates with the decorator pattern or other design patterns just because the name template appears in both contexts. The name collision is accidental. One is a compile-time type mechanism; the other is a run-time object structuring intent.

Frameworks are a set of cooperating classes designed for a particular kind of application such as compilers, graphical editors, business systems, or GUI applications. A framework implements the basic architecture and inverts control — the framework defines the main flow (event loop, validation loop, transaction loop) and calls application-supplied extensions at hook points. The developer adds application-specific operations, names, and state information and implements responsibilities and collaborations as the pattern prescribes, fitting into the framework's hooks. Real-world: compiler frameworks, GUI frameworks such as Swing's overall application structure (event dispatch thread, paint cycle, layout cycle), Eclipse or Spring as larger frameworks. The key sentence for exams: " framework calls you; you call a toolkit."

Patterns are medium-granularity design descriptions that can be used inside toolkits and frameworks. Toolkits and frameworks are larger reusable architectures; patterns are the recurring design decisions within them that standardize how problems like adapting interfaces, decorating components, encapsulating commands, deferring creation, and varying algorithms are solved. For example, Swing as a framework embeds Decorator (JScrollPane), Strategy (LayoutManager), Command (Action), Proxy (RMI stubs), and Factory (Collection.iterator()) as its extension points.

Comparison table — the level spectrum.

Reuse unit Granularity Control flow Example What you customize
Idiom Single class You call it for-loop with Iterator N/A
Toolkit Library of classes You call it C++ STL, java.util You pick classes
Pattern One design decision Either Adapter, Decorator, Strategy Roles + collaborations
Framework Application skeleton It calls you (inversion of control) Swing framework, compiler framework Implement hook methods / supply Strategy/Factory

One-sentence exam answer: toolkit = reusable classes the application calls; framework = cooperating classes for an application kind that calls the application's extensions via inversion of control; pattern = medium-granularity template for one problem inside either.

Scope — when each fits. Assumption: you understand who owns the main loop. Toolkit fit: you need independent utilities and want to stay in control. Framework fit: you are building a member of a family (all compilers need parsing + codegen; all GUI apps need event loop) and are willing to surrender the main flow to gain structure. Pitfall: trying to use a framework as a toolkit by ignoring its lifecycle and calling low-level pieces directly — it bypasses the hooks and breaks invariants. Another pitfall: calling generics "Template pattern" and answering a pattern question with template <class T> — that conflation loses marks.

14.7.2 Applying a Pattern in a Project — What Must Be Changed

Formalize — no pattern is copy-paste. A pattern cannot be copied verbatim. The textbook description gives abstract roles — Component, Decorator, Strategy, Context, Factory, Product — but a real project needs concrete domain names. Choose application-specific operation names, state information, and receiver types, then implement them with the responsibilities and collaborations described by the pattern. This adaptation step — renaming, adding domain state, wiring the actual receiver — is expected in every use. For example, the abstract Strategy.doWork() becomes LayoutManager.layoutContainer(Container) or ISalePricingStrategy.getTotal(Sale) or Comparator.compare(a,b); the abstract Creator.factoryMethod() becomes Collection.iterator() or Event.getSeeding(). The collaboration — context delegating to strategy, factory returning interface — stays identical; only names and state change. Production, framework usage, and architecture-level reuse all benefit when patterns are instantiated this way rather than pasted.

Visual intuition: picture a stencil (the pattern) with labeled cutouts: Component, Decorator. Next to it, three stenciled walls labeled Swing, I/O Streams, Pricing. Each wall shows the same stencil shape but with different paint colors and labels: JComponent / JScrollPane, InputStream / BufferedInputStream, Sale / PercentDiscountPricingStrategy. The stencil shape — aggregation plus interface — is constant; the paint — names, state — varies. Takeaway: patterns are stencil shapes, not finished walls.

Pitfalls — applying patterns blindly.

  • Literal copy: pasting Decorator and ConcreteDecorator class names unchanged instead of ImageProxy or BufferedInputStream — reviewers will flag low domain modeling.
  • Missing wiring: copying structure but forgetting to wire the receiver (command without a clipboard) so execute() does nothing.
  • Over-applying: turning every conditional into Strategy or every creation into Factory even when there is no family to vary.

14.7.3 Student Questions and Answers

Q: Can we reuse a pattern exactly as shown in a textbook example? A: No. The pattern description defines roles and collaborations, but application-specific operations, names, and state must be changed to fit the problem. Implement the responsibilities and collaborations as the pattern cuts them — meaning keep the delegation, aggregation, and interface relationships — just with the names and details of the current domain.

Example: the textbook decorates a JButton with CoolDecorator for mouse-over border erasing, but your editor project decorates a TextView with SyntaxHighlightDecorator that intercepts paint to colorize keywords before forwarding to the wrapped FileView. Same collaboration (wrap → forward → augment), entirely different names and state (keyword table vs mouse_over flag). The same holds for factories: the textbook NamerFactory branches on a comma; your ServicesFactory branches on a system property via reflection. The collaboration — factory returns ITaxCalculatorAdapter interface — is preserved; the parameter and loading mechanism change.

The professor's phrasing is precise: "change application specific operation names, state information, and receiver types, then implement responsibilities and collaborations as the pattern cuts them." That sentence is a ready-made exam answer for this Q&A.

14.7.4 Worked Examples

Example — Choosing among frameworks, toolkits, and patterns. Setup: a team must build a new code editor that supports multiple languages, image previews with lazy loading, and saving in multiple formats.

  • Using a toolkit means picking libraries for text buffers (javax.swing.text), parsing (ANTLR toolkit), and rendering and calling them explicitly: buffer.insert(pos, text); parser.parse(buffer); renderer.paint(g); — the application main owns the sequence.
  • Using a framework means starting from an editor framework that already provides the main loop, document model, and plugin points — for example Eclipse's editor framework or a Swing application framework that runs the event dispatch thread, document lifecycle, and menu validation. The team then fills in concrete editors for specific languages by implementing expected interfaces: class PythonEditor extends EditorFramework { void createSyntaxDecorator() { setDecorator(new SyntaxDecorator()); } }. The framework calls createSyntaxDecorator() at startup — inversion of control.
  • Using a pattern means deciding locally that saving files in multiple formats will be a Strategy (SaveStrategy with PdfSave / WordSave), or that editor commands will be Commands with execute() and enabled state (CopyCommand checking isEnabled()), or that image previews will use a Proxy for lazy loading (ImageProxy deferring loadBytes() until paintIcon). Framework choice sits above pattern choice, but pattern knowledge guides how the framework's extension points are implemented — each extension point is itself a pattern instance.

Decision takeaway: if the problem says "application calls library" that is toolkit; "framework supplies Half the editor and calls your plugin" that is framework; "local decision to vary algorithm vs add behavior vs defer creation" that is pattern selection within either.

14.7.5 Industry Applications

Real-world: C++ standard library as the canonical toolkit — vector, map, algorithm used by the application via direct calls. Compiler and GUI frameworks as architectures that embed patterns — Swing's JComponent hierarchy, Eclipse or NetBeans Platform where editors are framework plugins. Collection and GUI libraries where pattern instances — factory for creation (Collection.iterator()), strategy for layouts (LayoutManager), proxy for remote access (RMI stubs, EJB pool proxies), command for actions (Action), decorator for views (BufferedInputStream, JScrollPane) — already appear as documented extension points. Any modern IDE, browser engine, or game engine is a framework that composes toolkits internally and exposes pattern-based hooks.

14.7.6 Exam Notes

Exam note: Expect the distinction among toolkits, templates, and frameworks to be tested as a direct definition question. State how frameworks differ from toolkits: a framework supplies cooperating classes for a whole application kind and defines control flow via inversion of control, while a toolkit supplies independent classes the application calls. Then score the second half: patterns are not frameworks; they operate at medium granularity inside those larger reuse units — one local design decision, not an architecture. If the question shows code with template <typename T>, answer must distinguish language generics from design pattern intent and note they should not be confused.

Recap + bridge — reuse spans a spectrum from libraries you call (toolkits) through half-architectures that call you (frameworks); patterns live in the middle as the reusable decisions that structure both. You never paste a pattern; you rename its roles to your domain and keep its collaboration. Bridge to final section: with the full pattern vocabulary assembled — Adapter, Decorator, Command, Proxy, Strategy, Factory, Singleton — the lecture now turns to sharpening judgment: how to spot correct versus incorrect pattern instances and how to avoid traps that make similar choices look alike.

14.8 Pattern Identification Exercises and Common Traps

14.8.1 Exercise Type 1 — Is Singleton Used Correctly?

A fragment was presented that appeared to name a singleton but was not a correct implementation. The diagnosis is to check two things. Naming alone does not make a singleton. The question is whether the code can actually return the single instance through a public access method. If the instance is created inside a class but there is no public method such as getInstance() that allows clients to reach it, there is no way to obtain the object. A correct singleton must expose the instance through a public access method; otherwise the creation is isolated and unusable.

Worked diagnosis — naming versus access. Consider a fragment that shows a class with a field private static Singleton instance = new Singleton(); but with private Singleton getInstance() or no accessor at all, and a variable named singleton in client code.

Step-by-step check:

  1. Check instance storage: is there a static private field instance? Yes, but that alone is insufficient.
  2. Check access method: is there public static Singleton getInstance() that returns instance (with lazy or eager initialization, optionally synchronized for threads)? Here it is missing or non-public, so callers cannot write Singleton.getInstance().
  3. Check construction guard: is the constructor private Singleton() {} so no one else can new Singleton()? If constructor is public, any number of instances can be made, also violating "exactly one."

Conclusion: the fragment claims "singleton" but clients have no legal path to the one instance. The correct singleton template from the companion is:

public class ServicesFactory {
    private static ServicesFactory instance = null; // or eager new
    private ServicesFactory() {} // guard
    public static synchronized ServicesFactory getInstance(){
        if(instance==null) instance = new ServicesFactory();
        return instance;
    }
}

The note that getInstance must be public, and that without it the code is not a singleton, is the direct answer expected. Answer highlighted: naming a field singleton never satisfies Singleton; only public static getInstance() with private constructor does.

Q: Does naming a variable singleton mean the pattern is present? A: No. Check whether the instance can be returned to a caller. If the instance is created inside the class but there is no public method such as getInstance() for clients to access it — or if the method is private/protected — the fragment does not implement Singleton correctly regardless of the name used. A second check: the class must guarantee exactly one instance is allowed; a public constructor or failure to guard instantiation also breaks it. This is the most common surface trap in the exercise set.

Pitfall — Singleton look-alikes.

  • Public static field public static Singleton instance without a method is not Singleton as defined (though it technically ensures one instance via public access, the GoF form requires getInstance() and private constructor for lazy control).
  • Double-checked without synchronization in multithreaded code.
  • Making all methods static instead of instance methods — the companion warns this prevents subclassing and remote-enabling.

14.8.2 Exercise Type 2 — Does This Fragment Implement Factory Method?

Worked diagnosis — XMLReader helper versus true Factory Method. Fragment shown: a helper class with a single method that creates a single XMLReader instance and returns it. It was offered as a factory method. Verdict: not a Factory Method.

Reasoning in four checks:

  1. Product family: does the design serve a family of products with a common interface — for example Shape with Circle/Rectangle, Iterator with ListIterator/HeapIterator, Seeding with StraightSeeding/CircleSeeding? The XMLReader fragment serves one product, no family.
  2. Creator hierarchy: does a base Creator declare abstract Product factoryMethod() and do concrete creators override to decide which product to create? The fragment has no such hierarchy; it is a single helper class with one create method.
  3. Subclass decision: does the decision live in subclasses (polymorphism) rather than in an if on a parameter inside one class? The fragment has no subclasses.
  4. Comparison to canonical case — Iterator: Collection.iterator() is a factory method because Collection declares it and LinkedList, PriorityQueue each implement it to return their own iterator type. The whole hierarchy participates.

The teaching point is that it is hard to judge at first, because isolated topics look simple but the correctness test is whether the responsibilities and collaborations for the whole pattern are present, not whether a single method creates an object. Many fragments implement a pattern only partially — getting one creation step right without the surrounding hierarchy — and the lecture warns this partial implementation is the recurring trap. Answer highlighted: merely creating one XMLReader in a helper is a simple helper or at best a Simple Factory helper, not Factory Method; Factory Method requires a Creator hierarchy where each ConcreteCreator decides the ConcreteProduct via polymorphism.

Scope — when to claim Factory Method. Assumption: you have a Creator hierarchy that wishes to defer instantiation to subclasses. Check: look for abstract Product create...() in a base class and ConcreteCreatorA/B overriding to return ConcreteProductA/B typed to the interface. If you see only if(type.equals(...)) return new ... inside one class, that is Simple Factory, not Factory Method — still a factory, but not the GoF Factory Method.

14.8.3 Exercise Type 3 — Which Pattern Fits a Description?

Worked diagnosis — matching description to pattern by intent phrases. A third exercise type asks to choose the pattern that fits a given problem description, often presented as confusingly similar choices. The lecture's recommended reading method is to underline intent phrases and map them via the vocabulary built across 14.1–14.7.

Mapping drill with lecture phrases:

  • "existing class whose services are wanted but interfaces do not match; convert target method every call to source; plug-and-socket mismatch" → Adapter.
  • "many components (buttons, text boxes, labels) need scroll bars/borders/colors for any of them; after decoration still behaves as a component; augment paint() by calling wrapped paint()" → Decorator (not Composite, which would say "compose into tree of parts-wholes").
  • "execute() together with isEnabled()/setEnabled()/putValue()/icon, history as list for undo/redo" → Command.
  • "delay instantiation until user clicks tab / reaches image; expensive to load; client should not be affected; same interface, hold reference, sleep before delegating" → Proxy.
  • "family of related algorithms (encryption, compression, layout, comparator, file save formats) each in its own class; client supplies strategy object at run time; context calls layoutContainer / compare / getTotal" → Strategy.
  • "hide instantiation logic; parameter tells which subtype; return through common interface; add caching behind it" → Simple Factory.
  • "define creation interface but let subclasses decide; iterator() over linked list versus priority queue each returns its own iterator class" → Factory Method.

Example confusing prompt: "The system needs to save files and the user wants to choose PDF versus Word at run time without changing the file interface." Correct is Strategy (family of save algorithms, client supplies PdfSave strategy), not Factory, even though a factory could create the strategy. The factory is the means; the varying-algorithm intent makes it Strategy. Conversely, "choose whether to create a Circle or Square based on a string" is Factory because the variation is which object is created.

The advice is to look for those repeated discriminators — whether the need is to adapt an incompatible interface, enhance a component while still using it as a component, encapsulate a request with state and history, defer creation or access behind the same interface, vary an algorithm family, or hide creation logic behind a common interface. Confusion is expected and is itself the learning goal; the exercise deliberately presents side-by-side similar choices to force intent-based reading rather than diagram matching. The next session is the last, and students were asked to study these discriminators, bring questions about pattern use, and prepare for a discussion of design metrics and measuring design quality.

Exam note — identification strategy. Do not match diagrams; match intent phrases. For each description, (1) underline the verb that states the need — convert, enhance, encapsulate-request, defer, vary-algorithm, hide-creation; (2) check structural cue — same interface versus changed interface; (3) confirm with example signal — JScrollPane/FilterInputStream for Decorator, Action/isEnabled for Command, paintIcon/tab-click for Proxy, LayoutManager/Comparator for Strategy, getNamer/getShape for Simple Factory, iterator()/getSeeding() for Factory Method. Common pitfall: answering "Singleton" because the word singleton appears in a variable name — always verify public getInstance() and private constructor. Quick self-check: after choosing, write one line "Intent is ..., therefore ..." — if you cannot fill the intent clause from the prompt, reconsider.

Recap — pattern judgment is a two-step test: does the full set of responsibilities and collaborations for the pattern appear, and does the intent phrase in the prompt match that pattern's purpose? Partial implementation and name-only claims fail both tests. Bridge — mastering these traps turns the earlier isolation learning (one pattern at a time) into reliable combination — the goal for the final lecture on design metrics, where these patterns will be measured, not just named.

Exam Guidance Summary

  • Intent drives pattern choice. Problems that mention converting one plug into another, existing services that are not compatible, and converting target to source point to Adapter. Problems that require adding scroll bars, borders, or other visual or behavioral enhancements without changing component usage point to Decorator. Problems that mention execute(), isEnabled(), setEnabled(), putValue(), icons, and undo/redo history point to Command. Problems that mention delaying instantiation, expensive loading until viewed or until a tab is clicked, or deferring remote connections point to Proxy. Problems that describe a family of algorithms such as encryption, compression, layout, comparator, or file save formats and the ability to change the algorithm at run time point to Strategy. Problems that seek to hide creation logic, expose only a common interface, and choose among several possible classes based on data point to Factory and Factory Method — the latter via a Creator hierarchy where each ConcreteCreator decides the ConcreteProduct, as with iterator() over singly linked lists, doubly linked lists, stacks, queues, and priority queues.
  • Know how to compare a constructor with a factory method: a constructor has the same name as the class and, when public, exposes creation — caller must new ConcreteClass(...); a factory method hides that logic and returns objects through a common interface, letting subclasses or a parameter-driven factory decide the concrete type. Be ready to explain why merely creating a single XMLReader does not satisfy Factory Method (no product family, no Creator hierarchy, single helper only), while iterator() over singly linked lists, doubly linked lists, stacks, queues, and priority queues is a canonical Factory Method because each collection supplies its own creation method behind a shared Iterator abstraction with hasNext() and next(). The Simple Factory versus Factory Method contrast — if(type) inside one class versus abstract create() overridden by subclasses — is the scored discriminator.
  • Distinguish diagrams from intent. Decorator and Composite share a diagram shape — shared abstraction, concrete component, and an aggregating wrapper — but Composite composes a tree (iterate over many children) while Decorator enhances a single object (forward to one wrapped component plus augmentation). Naming alone, such as calling a variable singleton, does not satisfy a pattern; for Singleton check that a public static getInstance() actually returns the one instance and that the constructor is private, and for any pattern check that the full set of responsibilities and collaborations is present, not just a fragment or a name.
  • The session emphasized downloading and running small code examples for Decorator via JScrollPane decorating JTextArea (new JScrollPane(myTextArea) and new SlashDecorator(new CoolDecorator(new JButton(...))) plus FilterInputStream chains), Command via Action/AbstractAction greeting actions and cut/copy/paste enable logic with undo history as a Vector<Command> + unDo(), Proxy via image proxies that implement paintIcon() with MediaTracker + delay and tab-deferred creation plus RMI stubs, Strategy via LayoutManager variants BorderLayout/GridLayout/BoxLayout with layoutContainer(Container) and Comparator variants (CompareById/Name/Age) plus file-save/pricing strategies, and Factories via shape factories (getShape(type)), name formatting factories using comma conventions (NamerFactory.getNamer), and honorific Mr./Ms. selection. Time constraints meant coding parts were assigned for independent practice rather than live demonstration; prepare to discuss how each execution path forwards to the wrapped or created object and augments or selects behavior. Exam answers should trace one such path — for example Decorator's paintwrapped.paint → augment, or Command's isEnabled guard → execute → append to history.
  • Series guidance: the next class is the last one. Review the pattern vocabulary — how to use each pattern, how they differ — and the differences among toolkits (application calls library, e.g., C++ STL), generic templates (language compile-time type mechanism, not a design pattern), and frameworks (cooperating classes for an application kind that invert control and call your extensions). The following topic will cover design metrics and measuring design quality — cohesion, coupling, and protected variation made measurable — and will build directly on the ability to name which pattern is present and why. Attendance and preparation were requested, and asking peers to attend was encouraged.

Key Industry Applications

Real-world: Java Swing toolkit — JComponent, JTextArea, JButton, JScrollPane for component decoration (new JScrollPane(myTextArea) and FilterInputStream chains like BufferedInputStream); Action, AbstractAction, ActionListener for commands (cut/copy/paste with isEnabled and undo histories); Container together with LayoutManager, BorderLayout, GridLayout, BoxLayout for strategies (every GUI validates via layoutContainer); and collection iterators hasNext()/next() over singly linked lists, doubly linked lists, priority queues, stacks, and queues as Factory Methods (collection.iterator()).

Real-world: Editor and GUI commands — cut, copy, paste with enabled/disabled state driven by Clipboard, and undo/redo over a List<Command> in word processors, drawing programs, and IDEs where each stroke or edit is an object that remembers its DrawData for reversal.

Real-world: Deferred loading — web pages that load images and videos only when scrolled into view or when a tab is clicked via ImageProxy.paintIcon with MediaTracker and a monitor thread; enterprise tabs that defer RealChart creation; and remote proxies in Java RMI and EJB connection pools that defer connection establishment until first method invocation.

Real-world: Remote access — Java Remote Method Invocation where a local stub/proxy stands in for a remote subject, implements the same service interface, and defers socket creation and marshaling until requestApproval or fetch is invoked, hiding latency behind the Subject interface.

Real-world: Algorithm families — file save formats such as PDF and word-processor formats, compression (Zip/Rar) and encryption (AES) variants, sorting strategies via Comparator applied through Collections.sort(list, comparator) that sorts by id, name, age, or city, plus shape factories (ShapeFactory.getShape) and name-formatting factories that hide branching on parameters such as type strings, honorific Mr. versus Ms., or comma-separated last-name-first conventions (NamerFactory.getNamer).

Real-world: Adapter for external-system integration — NextGen POS ITaxCalculatorAdapter / IAccountingAdapter adapting TaxMaster versus GoodAsGoldTaxPro and SAP via SOAP over HTTPS, selected by a reflective ServicesFactory singleton reading class names from system properties.

Real-world: Reuse architectures — C++ standard library as a toolkit example (application calls library) and compiler, GUI, and business-system frameworks as larger architectures that invert control and embed the above patterns as extension points; understanding whether you call the library or the framework calls you determines whether you are using a toolkit or a framework, and patterns are the vocabulary inside both.

OODAP Lecture 14 notes · Designing with Patterns — Decorator, Command, Proxy, Strategy, and Factory

Object Oriented Design, Analysis and Programming Architecture· postgraduate· 2026-08-19

Sections Breakdown

114.1 Good Design, Pattern Granularity, and Adapter Recap

Defines good design via cohesion and coupling, places patterns at medium granularity, and recaps Adapter as interface translation for wanted services.

214.2 Decorator Pattern — Adding Visual and Behavioral Enhancement Without Changing Core Behavior

Decorator wraps a Component to add per-instance enhancements like scroll bars or borders while preserving Component transparency and enabling stacking.

314.3 Command Pattern — Turning Requests into Objects that Carry State and Behavior

Command reifies a request as an object carrying execute() plus enabled/icon/history state, decoupling invoker from receiver and enabling undo.

414.4 Proxy Pattern — Controlling Access and Deferring Cost While Preserving the Client View

Proxy stands in for a RealSubject behind the same Subject interface to defer creation, cache, or control access without client visibility.

514.5 Strategy Pattern — Encapsulating Interchangeable Algorithms

Strategy encapsulates a family of interchangeable algorithms behind a common interface so Context can delegate and clients can swap at run time.

614.6 Factory Patterns, Factory Method, and Iterator as a Factory Method

Simple Factory centralizes parameter-driven creation behind an interface; Factory Method defers instantiation to subclasses, with Iterator as canonical example.

714.7 Toolkits, Templates, Frameworks, and How Patterns Fit Reuse

Distinguishes toolkits (app calls library), C++ templates (compile-time generics), frameworks (framework calls app via inversion of control), and patterns as medium-granularity decisions inside both.

814.8 Pattern Identification Exercises and Common Traps

Exercises test full responsibilities vs partial implementation and name-only claims: Singleton needs public getInstance, Factory Method needs Creator hierarchy, pattern choice needs intent phrases.

9Exam Guidance Summary

Consolidates exam signals per pattern: Adapter plug-socket, Decorator wrap-and-augment, Command execute+isEnabled+undo, Proxy defer, Strategy algorithm family, Factory hide creation.

10Key Industry Applications

Industry mapping: Swing decoration/command/strategy, I/O decorator streams, RMI/EJB proxies, collection comparators and iterators, POS adapters and factories.

Postgraduate students in Object Oriented Design, Analysis and Programming

Exam Revision Notes

Below is the distilled, exam-ready core. Every entry comes from the full explanation above. Use this section for rapid review; return to the main notes when a point needs more context.

Good Design, Pattern Granularity, and Adapter Recap

Must-know: Adapter requires wanted services exist but interfaces mismatch in same context; translate target to source without rewriting adaptee

⚠️ Top pitfall: Matching diagrams instead of intent; naming a class Adapter without translating the interface

Self-check: Given an existing tax calculator with wrong signatures but correct behavior, which pattern and what must the adapter implement?

Connects to: 14.2, 14.4

Decorator Pattern — Adding Visual and Behavioral Enhancement Without Changing Core Behavior

Must-know: Decorator holds a Component and implements Component; paint() forwards to wrapped paint() then augments; result still a Component for stacking

⚠️ Top pitfall: Confusing Decorator with Composite (same diagram, different intent) or with C++ templates; replacing instead of augmenting by forgetting to forward

Self-check: JScrollPane wrapping JTextArea must still be addable to a JFrame as what type, and what must its paint method call?

Connects to: 14.4, 14.7

Command Pattern — Turning Requests into Objects that Carry State and Behavior

Must-know: Command bundles behavior and state (isEnabled, icon, history); enable belongs to command, undo needs list of command objects with receiver snapshots

⚠️ Top pitfall: Storing enabled on button instead of command; calling execute without isEnabled guard; undo without saving parameters

Self-check: Why is Paste gray until Copy, and why must undo store command objects not just function pointers?

Connects to: 14.5, 14.7

Proxy Pattern — Controlling Access and Deferring Cost While Preserving the Client View

Must-know: Proxy and RealSubject share same interface; proxy holds reference to real, adds lazy creation or access check before delegating same method

⚠️ Top pitfall: Confusing Proxy (controls access timing) with Decorator (adds visible responsibility) or Adapter (changes interface)

Self-check: What phrases signal Proxy and what structural constraint about interfaces must an answer state?

Connects to: 14.2, 14.6

Strategy Pattern — Encapsulating Interchangeable Algorithms

Must-know: Each algorithm in its own class sharing Strategy interface; Context holds strategy and calls layoutContainer/compare/getTotal; client supplies and swaps

⚠️ Top pitfall: Keeping if/switch inside Context instead of delegating; forcing unrelated algorithms into one interface; confusing Strategy with Command or State

Self-check: Map Container, LayoutManager, BorderLayout, and layoutContainer to Context/Strategy/ConcreteStrategy/method for Strategy

Connects to: 14.3, 14.6

Factory Patterns, Factory Method, and Iterator as a Factory Method

Must-know: Constructor exposes concrete creation; Factory hides it behind Product interface — Simple Factory via if on data, Factory Method via subclass polymorphism; iterator() is Factory Method

⚠️ Top pitfall: Calling a single XMLReader helper a Factory Method; leaking concrete types in variable declarations; not using the factory at call sites

Self-check: Why is Collection.iterator() a Factory Method but a helper that creates one XMLReader is not?

Connects to: 14.1, 14.5

Toolkits, Templates, Frameworks, and How Patterns Fit Reuse

Must-know: Toolkit: reusable classes app calls; Framework: cooperating classes for app kind that inverts control; Pattern: medium-granularity local decision; C++ templates are language generics not design patterns

⚠️ Top pitfall: Calling framework a toolkit or labeling C++ template as Template pattern; copying pattern verbatim without renaming to domain

Self-check: Who calls whom for toolkit vs framework, and where do patterns sit in granularity?

Connects to: 14.2, 14.5

Pattern Identification Exercises and Common Traps

Must-know: Singleton requires public static getInstance with private constructor; single XMLReader helper is not Factory Method; choose pattern by intent phrases not diagram shape

⚠️ Top pitfall: Naming alone satisfies pattern; partial collaboration counts; diagram matching without intent

Self-check: A fragment names a field singleton but getInstance is private — does it satisfy Singleton? Why?

Connects to: 14.1, 14.6

Was this lecture useful?

Loading comments…
🤖

BitsNotes AI Assistant

Subject Notes Assistant

Configure AI Chat

Choose how to access the chatbot
Have your own API key?

Switch to "Bring Your Own Key" tab above for unlimited access with any OpenAI-compatible provider.

🔑 Enter API key above to fetch live models from provider, or enter model name manually.
OpenAI-Compatible API Support

Choose any provider preset (Gemini, DeepSeek, Kimi, GLM, MiniMax, Qwen, OpenAI, Groq, Ollama, etc.) or enter a custom endpoint URL.

Security & Privacy First

Your API key is sent directly from your browser to your specified provider. BitsNotes servers never store or see your key.