2IRR00 · resource
Design pattern catalog
Intent, structure, Java examples, use cases, and exam traps.
Singleton
Restrict a class to a single instance and provide a global access point to it.
Family: Creational
class Singleton {
private static final Singleton instance = new Singleton(); // eager
private Singleton() {}
public static Singleton getInstance() { return instance; }
}
// Lazy (NOT thread-safe):
class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) instance = new LazySingleton(); // race condition!
return instance;
}
}When to use
- Exactly one instance must coordinate access (logger, config, file system, window manager).
- To store/manage shared variables instead of true globals.
- Often used to implement other patterns (e.g. a singleton facade).
Exam traps
- Problems: introduces global access (misuse), violates single-responsibility, raises coupling, hurts testability/extensibility.
- WRONG distractors: 'private constructor makes it hard to reuse', 'existing only once is never good'.
- Lazy is not thread-safe (two threads both see null); fix with eager init or synchronized getInstance().
- Code edit: private static instance + private constructor + public static getInstance(); instance need not be final.
Invalid learning block
media.gallery · singleton-slides
Factory / Abstract Factory
Define an interface for creating objects but defer the concrete instantiation to subclasses, decoupling creation from concrete classes.
Family: Creational
abstract class Vehicle { abstract String getName(); }
class Car extends Vehicle { String getName() { return "Car"; } }
class Bike extends Vehicle { String getName() { return "Bike"; } }
abstract class AbstractFactory { abstract Vehicle create(int id); }
class VehicleFactory extends AbstractFactory {
Vehicle create(int id) {
return switch (id) {
case 1 -> new Car();
case 2 -> new Bike();
default -> throw new IllegalArgumentException("no such id");
};
}
}When to use
- Object types are unknown at coding time, or subclasses should decide what to create.
- Creating UI / game-engine elements, loggers, plug-ins.
- Abstract Factory: create families of related objects (EU vs US factory).
Exam traps
- 'new' couples to a concrete class and violates dependency inversion — the factory returns an abstraction instead.
- Static-type subtlety: the factory returns the abstract Product; `Car c = factory.create(...)` fails without a cast (returned type is Vehicle).
- Don't overuse — adds abstraction/complexity.
Invalid learning block
media.gallery · factory-slides
Decorator
Attach additional responsibilities to an object dynamically (at runtime) by wrapping it — a flexible alternative to subclassing.
Family: Structural
interface Shape { void draw(); }
class Circle implements Shape { public void draw(){ System.out.print("Circle"); } }
abstract class ShapeDecorator implements Shape {
protected Shape inner;
ShapeDecorator(Shape s){ this.inner = s; }
public void draw(){ inner.draw(); }
}
class BlueBorder extends ShapeDecorator {
BlueBorder(Shape s){ super(s); }
public void draw(){ System.out.print("Blue "); inner.draw(); }
}
// new BlueBorder(new Circle()).draw(); → "Blue Circle"When to use
- Add/remove behaviour per object at runtime (GUI elements, I/O streams).
- Avoid deep inheritance hierarchies / the diamond problem.
Exam traps
- Decorates an OBJECT at runtime, not a class at design time.
- Uses aggregation (wrapped part can exist alone).
- Object identity: decorated != base (== is false); override equals()/hashCode() to compare base objects.
- Wrapping order can matter.
Invalid learning block
media.gallery · decorator-slides
Facade
Provide a single, unified interface that hides the complexity of a larger unit/subsystem.
Family: Structural
class ComputationFacade { // often a singleton
private static final ComputationFacade api = new ComputationFacade();
private ComputationFacade() {}
public static ComputationFacade getAPI() { return api; }
public boolean createElement(ShapesEnum s, ColorsEnum c, BorderEnum b) {
// hides the order/details of commands, factories, decorators ...
return true;
}
}When to use
- Wrap any API/library you depend on.
- Define a single access point and constrain call order.
- SwingWorker is itself a facade over Thread/Runnable.
Exam traps
- Facade vs Blob: facade exposes a MINIMAL interface and delegates (no behaviour of its own); a blob centralises data + behaviour.
- Can over-hide and add complexity if misused; must also be maintained.
Invalid learning block
media.gallery · facade-slides
Command
Encapsulate a request as an object with all info needed to execute it later, decoupling the request data from the moment of calling.
Family: Behavioral
interface Command {
void execute(); void revert();
default boolean canBeReverted(){ return true; }
}
class LightOnCommand implements Command {
Light light;
LightOnCommand(Light l){ light = l; }
public void execute(){ light.switchOn(); }
public void revert(){ light.switchOff(); }
}
// undoStack/redoStack of Command drive do/undo/redoWhen to use
- Undo/redo
- Queues & logging
- GUI buttons / menu items
- Delaying or scheduling execution
Exam traps
- Ideas: encapsulating info to execute a request at any time ✔; decoupling data from moment of calling ✔.
- WRONG distractors: 'requesting the user to do something', 'sending a request to different systems'.
- Not every command is undoable — guard with canBeReverted(). Compound commands use Composite.
Invalid learning block
media.gallery · command-slides
Observer
Define a one-to-many dependency so that when one subject changes, all its observers are notified and update automatically.
Family: Behavioral
interface Subject {
void addObserver(Observer o); void removeObserver(Observer o);
void notifyObservers();
}
interface Observer { void update(Person p); }
// PUSH: subject sends data
public void notifyObservers(){
for (Observer o : observers) o.update(this);
}
// PULL: o.update(); then observer calls subject.getState()When to use
- Update views on model change (MVC/GUIs)
- Distributed event handling
- Notifications
Exam traps
- PUSH: subject sends the data → more coupling, often too much data.
- PULL: subject only notifies; each observer pulls what it needs via getters → less coupling, possibly less efficient, observers must work out what changed.
- Both need some knowledge of the subject; coupling drops if they know it via an abstract type.
- Remember to call notifyObservers() after a state change.
Invalid learning block
media.gallery · observer-slides
Strategy
Encapsulate a family of interchangeable algorithms and let the client choose/swap one at runtime.
Family: Behavioral
Also known as: Policy
interface Payment { boolean process(Transaction t); }
class CreditCardPayment implements Payment { public boolean process(Transaction t){ /*...*/ return true; } }
class BankTransferPayment implements Payment { public boolean process(Transaction t){ /*...*/ return true; } }
class PaymentService {
private Payment strategy;
PaymentService(Payment s){ strategy = s; }
void setStrategy(Payment s){ strategy = s; } // swap at runtime
void pay(Transaction t){ strategy.process(t); }
}When to use
- Payment methods
- Compression/encryption
- File-format conversion
- Output formatting (XML/JSON/YAML)
Exam traps
- Composition (interface), not inheritance; same GOAL, different interchangeable implementations chosen by the client.
- Interface must declare all methods even if some strategies ignore them; overkill for very few strategies.
- Identical structure to State — distinguished only by intent.
Invalid learning block
media.gallery · strategy-slides
Iterator
Provide sequential access to the elements of an aggregate object without exposing its underlying representation.
Family: Behavioral
interface Aggregate<T> { Iterator<T> createIterator(); }
interface Iterator<T> { boolean hasNext(); T next(); }
class BinaryTree implements Aggregate<Node> {
public Iterator<Node> createIterator(){ return new InOrderIterator(root); }
}
// while (it.hasNext()) process(it.next());When to use
- Traverse lists/trees/graphs in a defined order
- Decouple traversal from storage
- Java Collections require iterator()
Exam traps
- Different traversal orders = different iterators (use Strategy to choose).
- Don't modify the structure directly while looping; update the iterator if structure changes.
- Pure overhead on fixed/simple structures.
Invalid learning block
media.gallery · iterator-slides
Template Method
Define the skeleton of an algorithm in a base class, letting subclasses refine specific steps without changing the structure.
Family: Behavioral
abstract class LoggerTemplate {
public void logMsg(Message m){ // template method (skeleton)
String s = formatMsg(m);
write(s);
}
protected abstract String formatMsg(Message m); // must override
protected abstract void write(String s); // must override
}
class ConsoleLogger extends LoggerTemplate {
protected String formatMsg(Message m){ return m.getMessage(); }
protected void write(String s){ System.out.println(s); }
}When to use
- AbstractList & framework templates
- Games
- Loggers
- Data validation
Exam traps
- Inheritance + class-level + static (applies to all objects).
- Abstract steps MUST be overridden; concrete steps MAY be. A missing step can violate Liskov.
- Often combined with Strategy for parts needing composition (no multiple inheritance in Java).
Invalid learning block
media.gallery · template-method-slides
State
Allow an object to alter its behaviour when its internal state changes — it appears to change its class.
Family: Behavioral
interface PlayerState { void handle(Player p, Trigger t); }
class StandingState implements PlayerState {
public void handle(Player p, Trigger t){
if (t == Trigger.PRESS_UP) { /* jump */ p.setState(new JumpingState()); }
}
}
class Player {
private PlayerState state;
void reactToTrigger(Trigger t){ state.handle(this, t); }
void setState(PlayerState s){ state = s; }
}When to use
- Character control in games
- State machines
- Behaviour driven by a fixed set of internal states
Exam traps
- Same structure as Strategy — intent differs: behaviour can change COMPLETELY between states, and states are explicitly linked (the object drives transitions).
- Overkill for a small state machine (over-engineering); hardcoded transitions reduce flexibility.
Invalid learning block
media.gallery · state-slides
Model-View-Controller
Separate a GUI application into Model (data + logic), View (presentation/IO) and Controller (glue) — defines WHAT to separate, not HOW.
Family: Architectural
// Model: data + business logic (Person, Course ...)
// View: PersonPlainListView / PersonConciseView (display)
// Controller (often singleton): links input to model & view
class Controller {
private static final Controller c = new Controller();
public static Controller get(){ return c; }
public void addStudent(int id, String name, int year){ /* update model */ }
public void printPersonByID(int id){ /* use a view */ }
}When to use
- GUI/information systems
- Multiple/ swappable presentations of the same data
- Separating presentation from data
Exam traps
- Correct: separates model/view/controller but not HOW ✔; can use Decorator to modify views ✔.
- WRONG: 'must use the factory pattern'; 'the model manages view↔controller interactions' (the controller links them).
- Can incorporate Command, Decorator, Facade, Factory, Observer, Strategy.
Invalid learning block
media.gallery · mvc-slides