Skip to content

OOP Design Patterns

Problem: Ensure only one instance exists, global access.

Use Case: Config manager, logging service, DB Connection

classDiagram
    class Logger {
        -static Logger instance
        -Logger()
        +getInstance()
        +log()
    }
class Logger {
private static Logger instance;
private Logger() {}
public static Logger getInstance() {
if (instance == null) instance = new Logger();
return instance;
}
public void log(String msg) { System.out.println(msg); }
}

Problem: Create objects without exposing instantiation logic.

Use Case: Payment processor selection.

classDiagram
    class Payment {
        <>
        +pay(amount)
    }
    class PayPal
    class Stripe
    class PaymentFactory
    Payment <|.. PayPal
    Payment <|.. Stripe
    PaymentFactory --> Payment
interface Payment {
void pay(double amount);
}
class PayPal implements Payment {
public void pay(double a){ System.out.println("PayPal "+a); }
}
class Stripe implements Payment {
public void pay(double a){ System.out.println("Stripe "+a); }
}
class PaymentFactory {
static Payment getPayment(String type) {
return type.equals("paypal") ? new PayPal() : new Stripe();
}
}

Problem: Provide an interface for creating families of related objects.

Use Case: GUI toolkit (Windows vs Mac).

classDiagram
    class GUIFactory {
        <>
        +createButton()
    }
    class WinFactory
    class MacFactory
    class Button {
        <>
        +render()
    }
    class WinButton
    class MacButton
    GUIFactory <|.. WinFactory
    GUIFactory <|.. MacFactory
    Button <|.. WinButton
    Button <|.. MacButton
interface Button { void render(); }
class WinButton implements Button {
public void render(){ System.out.println("Win Button"); }
}
class MacButton implements Button {
public void render(){ System.out.println("Mac Button"); }
}
interface GUIFactory { Button createButton(); }
class WinFactory implements GUIFactory {
public Button createButton(){ return new WinButton(); }
}
class MacFactory implements GUIFactory {
public Button createButton(){ return new MacButton(); }
}

Problem: Convert one interface into another.

Use Case: Old payment library → New checkout system.

classDiagram
    class LegacyPayment {
        +makePayment()
    }
    class ModernPayment {
        <>
        +pay()
    }
    class Adapter {
        -LegacyPayment legacy
        +pay()
    }
    Adapter ..|> ModernPayment
    Adapter --> LegacyPayment
class LegacyPayment {
void makePayment(){ System.out.println("Legacy Paid"); }
}
interface ModernPayment {
void pay();
}
class Adapter implements ModernPayment {
private LegacyPayment legacy = new LegacyPayment();
public void pay(){ legacy.makePayment(); }
}

Problem: Add responsibilities dynamically without altering code.

Use Case: Logging, compression, encryption for data streams.

classDiagram
    class Notifier {
        <>
        +send(msg)
    }
    class EmailNotifier
    class SMSDecorator
    Notifier <|.. EmailNotifier
    Notifier <|.. SMSDecorator
    SMSDecorator --> Notifier
interface Notifier {
void send(String msg);
}
class EmailNotifier implements Notifier {
public void send(String m){ System.out.println("Email: "+m); }
}
class SMSDecorator implements Notifier {
private Notifier wrap;
SMSDecorator(Notifier n){
wrap=n;
}
public void send(String m) {
wrap.send(m);
System.out.println("SMS: "+m);
}
}

Problem: Simplify a complex subsystem.

Use Case: Video conversion API.

classDiagram
    class VideoFacade {
        +convert()
    }
    class CodecLoader
    class VideoCompressor
    VideoFacade --> CodecLoader
    VideoFacade --> VideoCompressor
class CodecLoader {
void load(){ System.out.println("Codec Loaded"); }
}
class VideoCompressor {
void compress(){ System.out.println("Compressed"); }
}
class VideoFacade {
private CodecLoader loader = new CodecLoader();
private VideoCompressor comp = new VideoCompressor();
void convert(){ loader.load(); comp.compress(); }
}

Problem: One-to-many dependency.

Use Case: Order placed → notify email, update inventory.

classDiagram
    class Observer {
        <>
        +update(msg)
    }
    class EmailObserver
    class Order {
        +addObserver()
        +place()
    }
    Observer <|.. EmailObserver
    Order --> Observer
interface Observer {
void update(String msg);
}
class EmailObserver implements Observer {
public void update(String m){ System.out.println("Email: "+m); }
}
class Order {
List<Observer> observers = new ArrayList<>();
void addObserver(Observer o){ observers.add(o); }
void place(){ for(Observer o: observers) o.update("Order placed"); }
}

Problem: Choose algorithm at runtime.

Use Case: Different shipping cost strategies.

classDiagram
    class Shipping {
        <>
        +cost(weight)
    }
    class Air
    class Sea
    class Cart {
        -Shipping s
        +calc(weight)
    }
    Shipping <|.. Air
    Shipping <|.. Sea
    Cart --> Shipping
interface Shipping {
double cost(double w);
}
class Air implements Shipping {
public double cost(double w){ return w*10; }
}
class Sea implements Shipping {
public double cost(double w){ return w*5; }
}
class Cart {
Shipping s;
Cart(Shipping s){ this.s=s; }
double calc(double w){ return s.cost(w); }
}

Problem: Encapsulate requests as objects.

Use Case: Undo/redo in editors.

classDiagram
    class Command {
        <>
        +execute()
    }
    class LightOn
    class Remote {
        -Command cmd
        +setCommand()
        +press()
    }
    Command <|.. LightOn
    Remote --> Command
interface Command {
void execute();
}
class LightOn implements Command {
public void execute(){ System.out.println("Light ON"); }
}
class Remote {
Command cmd;
void setCommand(Command c){ cmd=c; }
void press(){ cmd.execute(); }
}

Problem: Define algorithm skeleton, let subclasses fill details.

Use Case: Report generation.

classDiagram
    class Report {
        +generate()
        +header()
        +body()* 
        +footer()
    }
    class SalesReport
    Report <|-- SalesReport
abstract class Report {
final void generate(){
header(); body(); footer();
}
void header(){
System.out.println("Header");
}
abstract void body();
void footer(){
System.out.println("Footer");
}
}
class SalesReport extends Report {
void body(){ System.out.println("Sales Data"); }
}