🏗️ Object-Oriented Programming

Design Patterns

"Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem." — Christopher Alexander

Creational Patterns

Singleton

Ensure a class has only one instance, provide global access.

class Singleton:
    _instance = None
    _lock = threading.Lock()

    def __new__(cls):
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self):
        if not hasattr(self, '_initialized'):
            self._initialized = True
            # initialization here
#include <mutex>
#include <memory>

class Singleton {
    Singleton() = default;
    ~Singleton() = default;
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

public:
    static Singleton& getInstance() {
        static std::unique_ptr<Singleton> instance;
        static std::once_flag flag;
        std::call_once(flag, []{ instance = std::make_unique<Singleton>(); });
        return *instance;
    }
};
public final class Singleton {
    private static volatile Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) instance = new Singleton();
            }
        }
        return instance;
    }
}
public sealed class Singleton {
    private static readonly Lazy<Singleton> _instance = 
        new Lazy<Singleton>(() => new Singleton());

    private Singleton() {}

    public static Singleton Instance => _instance.Value;
}

Factory Method

Define interface for creating objects, let subclasses decide which class to instantiate.

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def speak(self) -> str:
        pass

class Dog(Animal):
    def speak(self) -> str:
        return "Woof!"

class Cat(Animal):
    def speak(self) -> str:
        return "Meow!"

class AnimalFactory(ABC):
    @abstractmethod
    def create(self) -> Animal:
        pass

class DogFactory(AnimalFactory):
    def create(self) -> Animal:
        return Dog()

class CatFactory(AnimalFactory):
    def create(self) -> Animal:
        return Cat()
#include <memory>
#include <iostream>

class Animal {
public:
    virtual ~Animal() = default;
    virtual void speak() const = 0;
};

class Dog : public Animal {
public:
    void speak() const override { std::cout << "Woof!\n"; }
};

class Cat : public Animal {
public:
    void speak() const override { std::cout << "Meow!\n"; }
};

class AnimalFactory {
public:
    virtual ~AnimalFactory() = default;
    virtual std::unique_ptr<Animal> create() = 0;
};

class DogFactory : public AnimalFactory {
public:
    std::unique_ptr<Animal> create() override { return std::make_unique<Dog>(); }
};

class CatFactory : public AnimalFactory {
public:
    std::unique_ptr<Animal> create() override { return std::make_unique<Cat>(); }
};
interface Animal {
    void speak();
}

final class Dog implements Animal {
    @Override public void speak() { System.out.println("Woof!"); }
}

final class Cat implements Animal {
    @Override public void speak() { System.out.println("Meow!"); }
}

interface AnimalFactory {
    Animal create();
}

final class DogFactory implements AnimalFactory {
    @Override public Animal create() { return new Dog(); }
}

final class CatFactory implements AnimalFactory {
    @Override public Animal create() { return new Cat(); }
}
public interface IAnimal {
    void Speak();
}

public sealed class Dog : IAnimal {
    public void Speak() => Console.WriteLine("Woof!");
}

public sealed class Cat : IAnimal {
    public void Speak() => Console.WriteLine("Meow!");
}

public interface IAnimalFactory {
    IAnimal Create();
}

public sealed class DogFactory : IAnimalFactory {
    public IAnimal Create() => new Dog();
}

public sealed class CatFactory : IAnimalFactory {
    public IAnimal Create() => new Cat();
}

Abstract Factory

Provide interface for creating families of related objects.

from abc import ABC, abstractmethod

class Button(ABC):
    @abstractmethod
    def render(self) -> str: pass

class Checkbox(ABC):
    @abstractmethod
    def render(self) -> str: pass

class WinButton(Button):
    def render(self) -> str: return "[Windows Button]"

class MacButton(Button):
    def render(self) -> str: return "[Mac Button]"

class WinCheckbox(Checkbox):
    def render(self) -> str: return "[Windows Checkbox]"

class MacCheckbox(Checkbox):
    def render(self) -> str: return "[Mac Checkbox]"

class GUIFactory(ABC):
    @abstractmethod
    def create_button(self) -> Button: pass
    @abstractmethod
    def create_checkbox(self) -> Checkbox: pass

class WinFactory(GUIFactory):
    def create_button(self) -> Button: return WinButton()
    def create_checkbox(self) -> Checkbox: return WinCheckbox()

class MacFactory(GUIFactory):
    def create_button(self) -> Button: return MacButton()
    def create_checkbox(self) -> Checkbox: return MacCheckbox()
class Button { public: virtual ~Button() = default; virtual void render() = 0; };
class Checkbox { public: virtual ~Checkbox() = default; virtual void render() = 0; };

class WinButton : public Button { void render() override { std::cout << "[Windows Button]\n"; } };
class MacButton : public Button { void render() override { std::cout << "[Mac Button]\n"; } };
class WinCheckbox : public Checkbox { void render() override { std::cout << "[Windows Checkbox]\n"; } };
class MacCheckbox : public Checkbox { void render() override { std::cout << "[Mac Checkbox]\n"; } };

class GUIFactory {
public:
    virtual ~GUIFactory() = default;
    virtual std::unique_ptr<Button> createButton() = 0;
    virtual std::unique_ptr<Checkbox> createCheckbox() = 0;
};

class WinFactory : public GUIFactory {
public:
    std::unique_ptr<Button> createButton() override { return std::make_unique<WinButton>(); }
    std::unique_ptr<Checkbox> createCheckbox() override { return std::make_unique<WinCheckbox>(); }
};

class MacFactory : public GUIFactory {
public:
    std::unique_ptr<Button> createButton() override { return std::make_unique<MacButton>(); }
    std::unique_ptr<Checkbox> createCheckbox() override { return std::make_unique<MacCheckbox>(); }
};
interface Button { void render(); }
interface Checkbox { void render(); }

final class WinButton implements Button { public void render() { System.out.println("[Windows Button]"); } }
final class MacButton implements Button { public void render() { System.out.println("[Mac Button]"); } }
final class WinCheckbox implements Checkbox { public void render() { System.out.println("[Windows Checkbox]"); } }
final class MacCheckbox implements Checkbox { public void render() { System.out.println("[Mac Checkbox]"); } }

interface GUIFactory {
    Button createButton();
    Checkbox createCheckbox();
}

final class WinFactory implements GUIFactory {
    public Button createButton() { return new WinButton(); }
    public Checkbox createCheckbox() { return new WinCheckbox(); }
}

final class MacFactory implements GUIFactory {
    public Button createButton() { return new MacButton(); }
    public Checkbox createCheckbox() { return new MacCheckbox(); }
}
public interface IButton { void Render(); }
public interface ICheckbox { void Render(); }

public sealed class WinButton : IButton { public void Render() => Console.WriteLine("[Windows Button]"); }
public sealed class MacButton : IButton { public void Render() => Console.WriteLine("[Mac Button]"); }
public sealed class WinCheckbox : ICheckbox { public void Render() => Console.WriteLine("[Windows Checkbox]"); }
public sealed class MacCheckbox : ICheckbox { public void Render() => Console.WriteLine("[Mac Checkbox]"); }

public interface IGUIFactory {
    IButton CreateButton();
    ICheckbox CreateCheckbox();
}

public sealed class WinFactory : IGUIFactory {
    public IButton CreateButton() => new WinButton();
    public ICheckbox CreateCheckbox() => new WinCheckbox();
}

Builder

Separate construction from representation.

class Computer:
    def __init__(self, cpu: str, ram: str, storage: str, gpu: str = ""):
        self.cpu = cpu
        self.ram = ram
        self.storage = storage
        self.gpu = gpu

    def __str__(self) -> str:
        return f"Computer({self.cpu}, {self.ram}, {self.storage}, {self.gpu})"

class ComputerBuilder:
    def __init__(self):
        self._cpu = ""
        self._ram = ""
        self._storage = ""
        self._gpu = ""

    def cpu(self, val: str) -> 'ComputerBuilder':
        self._cpu = val
        return self

    def ram(self, val: str) -> 'ComputerBuilder':
        self._ram = val
        return self

    def storage(self, val: str) -> 'ComputerBuilder':
        self._storage = val
        return self

    def gpu(self, val: str) -> 'ComputerBuilder':
        self._gpu = val
        return self

    def build(self) -> 'Computer':
        return Computer(self._cpu, self._ram, self._storage, self._gpu)

# Usage
pc = ComputerBuilder().cpu("Intel i9").ram("32GB").storage("1TB SSD").gpu("RTX 4090").build()
#include <string>

class Computer {
    std::string cpu_, ram_, storage_, gpu_;
    Computer(std::string cpu, std::string ram, std::string storage, std::string gpu)
        : cpu_(cpu), ram_(ram), storage_(storage), gpu_(gpu) {}

    friend class Builder;

public:
    class Builder {
        std::string cpu_, ram_, storage_, gpu_;
    public:
        Builder& cpu(std::string v) { cpu_ = std::move(v); return *this; }
        Builder& ram(std::string v) { ram_ = std::move(v); return *this; }
        Builder& storage(std::string v) { storage_ = std::move(v); return *this; }
        Builder& gpu(std::string v) { gpu_ = std::move(v); return *this; }
        Computer build() { return Computer(cpu_, ram_, storage_, gpu_); }
    };
};
public final class Computer {
    private final String cpu, ram, storage, gpu;

    private Computer(Builder b) { this.cpu = b.cpu; this.ram = b.ram; this.storage = b.storage; this.gpu = b.gpu; }

    public static class Builder {
        private String cpu = "", ram = "", storage = "", gpu = "";
        public Builder cpu(String v) { this.cpu = v; return this; }
        public Builder ram(String v) { this.ram = v; return this; }
        public Builder storage(String v) { this.storage = v; return this; }
        public Builder gpu(String v) { this.gpu = v; return this; }
        public Computer build() { return new Computer(this); }
    }
}
public sealed class Computer {
    public string Cpu { get; }
    public string Ram { get; }
    public string Storage { get; }
    public string Gpu { get; }

    private Computer(Builder b) {
        Cpu = b.Cpu; Ram = b.Ram; Storage = b.Storage; Gpu = b.Gpu;
    }

    public sealed class Builder {
        public string Cpu { get; set; } = "";
        public string Ram { get; set; } = "";
        public string Storage { get; set; } = "";
        public string Gpu { get; set; } = "";
        public Computer Build() => new Computer(this);
    }
}

// Usage
var pc = new Computer.Builder()
    .Cpu("Intel i9").Ram("32GB").Storage("1TB SSD").Gpu("RTX 4090")
    .Build();

Structural Patterns

Adapter

Convert interface of class into another interface client expects.

from abc import ABC, abstractmethod
from typing import Dict

class LegacyXMLParser:
    def parse_xml(self, xml: str) -> Dict[str, str]:
        return {"data": "parsed from XML"}

class JSONParser(ABC):
    @abstractmethod
    def parse(self, json: str) -> Dict[str, str]: pass

class XMLToJSONAdapter(JSONParser):
    def __init__(self, parser: 'LegacyXMLParser'):
        self.parser = parser

    def parse(self, json: str) -> Dict[str, str]:
        xml = self.json_to_xml(json)
        return self.parser.parse_xml(xml)

    def json_to_xml(self, json: str) -> str:
        # conversion logic
        return "<xml>converted</xml>"
#include <map>
#include <string>
#include <memory>

class LegacyXMLParser {
public:
    std::map<std::string, std::string> parseXML(const std::string& xml) {
        return {{"data", "parsed from XML"}};
    }
};

class JSONParser {
public:
    virtual std::map<std::string, std::string> parse(const std::string& json) = 0;
    virtual ~JSONParser() = default;
};

class XMLToJSONAdapter : public JSONParser {
    std::unique_ptr<LegacyXMLParser> parser_;
public:
    explicit XMLToJSONAdapter(std::unique_ptr<LegacyXMLParser> p) : parser_(std::move(p)) {}
    std::map<std::string, std::string> parse(const std::string& json) override {
        std::string xml = jsonToXML(json);
        return parser_->parseXML(xml);
    }
    std::string jsonToXML(const std::string& json) { return "<xml>converted</xml>"; }
};
import java.util.*;

class LegacyXMLParser {
    public Map<String, String> parseXML(String xml) {
        return Map.of("data", "parsed from XML");
    }
}

interface JSONParser {
    Map<String, String> parse(String json);
}

class XMLToJSONAdapter implements JSONParser {
    private final LegacyXMLParser parser;
    public XMLToJSONAdapter(LegacyXMLParser p) { parser = p; }
    public Map<String, String> parse(String json) {
        String xml = jsonToXML(json);
        return parser.parseXML(xml);
    }
    String jsonToXML(String json) { return "<xml>converted</xml>"; }
}
using System.Collections.Generic;

public class LegacyXMLParser {
    public Dictionary<string, string> ParseXML(string xml) {
        return new Dictionary<string, string> { {"data", "parsed from XML"} };
    }
}

public interface IJSONParser {
    Dictionary<string, string> Parse(string json);
}

public class XMLToJSONAdapter : IJSONParser {
    private readonly LegacyXMLParser _parser;
    public XMLToJSONAdapter(LegacyXMLParser p) => _parser = p;
    public Dictionary<string, string> Parse(string json) {
        string xml = JsonToXml(json);
        return _parser.ParseXML(xml);
    }
    string JsonToXml(string json) => "<xml>converted</xml>";
}

Structural Patterns (continued)

Decorator

Attach additional responsibilities dynamically.

from abc import ABC, abstractmethod

class Coffee(ABC):
    @abstractmethod
    def cost(self) -> float: pass
    @abstractmethod
    def description(self) -> str: pass

class SimpleCoffee(Coffee):
    def cost(self) -> float: return 2.0
    def description(self) -> str: return "Simple coffee"

class CoffeeDecorator(Coffee):
    def __init__(self, coffee: Coffee):
        self._coffee = coffee

    def cost(self) -> float:
        return self._coffee.cost()

    def description(self) -> str:
        return self._coffee.description()

class MilkDecorator(CoffeeDecorator):
    def cost(self) -> float:
        return self._coffee.cost() + 0.5

    def description(self) -> str:
        return self._coffee.description() + ", Milk"

class SugarDecorator(CoffeeDecorator):
    def cost(self) -> float:
        return self._coffee.cost() + 0.2

    def description(self) -> str:
        return self._coffee.description() + ", Sugar"

# Usage
coffee = SimpleCoffee()
coffee = MilkDecorator(coffee)
coffee = SugarDecorator(coffee)
print(f"{coffee.description()} = ${coffee.cost()}")
#include <string>
#include <memory>
#include <iostream>

class Coffee {
public:
    virtual ~Coffee() = default;
    virtual double cost() const = 0;
    virtual std::string description() const = 0;
};

class SimpleCoffee : public Coffee {
    double cost() const override { return 2.0; }
    std::string description() const override { return "Simple coffee"; }
};

class CoffeeDecorator : public Coffee {
    std::unique_ptr<Coffee> coffee_;
public:
    explicit CoffeeDecorator(std::unique_ptr<Coffee> c) : coffee_(std::move(c)) {}
    double cost() const override { return coffee_->cost(); }
    std::string description() const override { return coffee_->description(); }
};

class MilkDecorator : public CoffeeDecorator {
public:
    explicit MilkDecorator(std::unique_ptr<Coffee> c) : CoffeeDecorator(std::move(c)) {}
    double cost() const override { return coffee_->cost() + 0.5; }
    std::string description() const override { return coffee_->description() + ", Milk"; }
};

class SugarDecorator : public CoffeeDecorator {
public:
    explicit SugarDecorator(std::unique_ptr<Coffee> c) : CoffeeDecorator(std::move(c)) {}
    double cost() const override { return coffee_->cost() + 0.2; }
    std::string description() const override { return coffee_->description() + ", Sugar"; }
};
interface Coffee {
    double cost();
    String description();
}

class SimpleCoffee implements Coffee {
    public double cost() { return 2.0; }
    public String description() { return "Simple coffee"; }
}

abstract class CoffeeDecorator implements Coffee {
    protected final Coffee coffee;
    CoffeeDecorator(Coffee c) { this.coffee = c; }
    public double cost() { return coffee.cost(); }
    public String description() { return coffee.description(); }
}

class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee c) { super(c); }
    public double cost() { return coffee.cost() + 0.5; }
    public String description() { return coffee.description() + ", Milk"; }
}

class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee c) { super(c); }
    public double cost() { return coffee.cost() + 0.2; }
    public String description() { return coffee.description() + ", Sugar"; }
}
using System;

public interface ICoffee {
    double Cost { get; }
    string Description { get; }
}

public sealed class SimpleCoffee : ICoffee {
    public double Cost { get; } = 2.0;
    public string Description { get; } = "Simple coffee";
}

public abstract class CoffeeDecorator : ICoffee {
    protected readonly ICoffee Coffee;
    protected CoffeeDecorator(ICoffee c) => Coffee = c;
    public virtual double Cost => Coffee.Cost;
    public virtual string Description => Coffee.Description;
}

public sealed class MilkDecorator : CoffeeDecorator {
    public MilkDecorator(ICoffee c) : base(c) {}
    public override double Cost => Coffee.Cost + 0.5;
    public override string Description => Coffee.Description + ", Milk";
}

public sealed class SugarDecorator : CoffeeDecorator {
    public SugarDecorator(ICoffee c) : base(c) {}
    public override double Cost => Coffee.Cost + 0.2;
    public override string Description => Coffee.Description + ", Sugar";
}

Behavioral Patterns

Observer

Define dependency so when one object changes, all dependents are notified.

from typing import Callable, List, Dict
from collections import defaultdict

class EventBus:
    def __init__(self):
        self._listeners: Dict[str, List[Callable]] = defaultdict(list)

    def subscribe(self, event: str, callback: Callable) -> None:
        self._listeners[event].append(callback)

    def publish(self, event: str, data: dict = None) -> None:
        for callback in self._listeners[event]:
            callback(data or {})

# Usage
bus = EventBus()
bus.subscribe("enemy_killed", lambda d: print(f"Enemy killed: {d.get('enemy', 'unknown')}"))
bus.publish("enemy_killed", {"enemy": "goblin", "xp": 10})
#include <functional>
#include <unordered_map>
#include <vector>
#include <any>

class EventBus {
    std::unordered_map<std::string, std::vector<std::function<void(const std::any&)>>> listeners_;
public:
    template<typename F>
    void subscribe(const std::string& event, F&& cb) {
        listeners_[event].push_back(std::forward<F>(cb));
    }

    void publish(const std::string& event, const std::any& data = {}) {
        for (auto& cb : listeners_[event]) cb(data);
    }
};

// Usage
EventBus bus;
bus.subscribe("enemy_killed", [](const std::any& data) {
    auto d = std::any_cast<std::map<std::string, int>>(data);
    std::cout << "Enemy killed: " << d.at("enemy") << " xp: " << d.at("xp") << "\n";
});
bus.publish("enemy_killed", std::map<std::string, int>{{"enemy", 0}, {"xp", 10}});
import java.util.*;
import java.util.function.Consumer;

public class EventBus {
    private final Map<String, List<Consumer<Map<String, Object>>>> listeners = new HashMap<>();

    public void subscribe(String event, Consumer<Map<String, Object>> cb) {
        listeners.computeIfAbsent(event, k -> new ArrayList<>()).add(cb);
    }

    public void publish(String event, Map<String, Object> data) {
        for (var cb : listeners.getOrDefault(event, List.of())) cb.accept(data);
    }
}

// Usage
EventBus bus = new EventBus();
bus.subscribe("enemy_killed", data -> 
    System.out.println("Enemy killed: " + data.get("enemy") + " xp: " + data.get("xp")));
bus.publish("enemy_killed", Map.of("enemy", "goblin", "xp", 10));
using System;
using System.Collections.Generic;

public class EventBus {
    private readonly Dictionary<string, List<Action<Dictionary<string, object>>>> _listeners = new();

    public void Subscribe(string evt, Action<Dictionary<string, object>> cb) {
        if (!_listeners.TryGetValue(evt, out var list))
            _listeners[evt] = new List<Action<Dictionary<string, object>>>();
        _listeners[evt].Add(cb);
    }

    public void Publish(string evt, Dictionary<string, object> data = null) {
        if (_listeners.TryGetValue(evt, out var list))
            foreach (var cb in list) cb(data ?? new());
    }
}

// Usage
var bus = new EventBus();
bus.Subscribe("enemy_killed", data =>
    Console.WriteLine($"Enemy killed: {data["enemy"]} xp: {data["xp"]}"));
bus.Publish("enemy_killed", new Dictionary<string, object> { ["enemy"] = "goblin", ["xp"] = 10 });

Strategy

Define family of algorithms, encapsulate each, make interchangeable.

from abc import ABC, abstractmethod

class CompressionStrategy(ABC):
    @abstractmethod
    def compress(self, data: str) -> str:
        pass

class ZipStrategy(CompressionStrategy):
    def compress(self, data: str) -> str:
        return f"zip:{data}"

class GzipStrategy(CompressionStrategy):
    def compress(self, data: str) -> str:
        return f"gzip:{data}"

class Compressor:
    def __init__(self, strategy: CompressionStrategy):
        self.strategy = strategy

    def compress(self, data: str) -> str:
        return self.strategy.compress(data)

    def set_strategy(self, strategy: CompressionStrategy) -> None:
        self.strategy = strategy

# Usage — swap at runtime!
c = Compressor(ZipStrategy())
print(c.compress("data"))  # "zip:data"
c.set_strategy(GzipStrategy())
print(c.compress("data"))  # "gzip:data"
#include <memory>
#include <string>

class CompressionStrategy {
public:
    virtual std::string compress(const std::string& data) = 0;
    virtual ~CompressionStrategy() = default;
};

class ZipStrategy : public CompressionStrategy {
public:
    std::string compress(const std::string& data) override { return "zip:" + data; }
};

class GzipStrategy : public CompressionStrategy {
public:
    std::string compress(const std::string& data) override { return "gzip:" + data; }
};

class Compressor {
    std::unique_ptr<CompressionStrategy> strategy_;
public:
    explicit Compressor(std::unique_ptr<CompressionStrategy> s) : strategy_(std::move(s)) {}
    std::string compress(const std::string& data) { return strategy_->compress(data); }
    void setStrategy(std::unique_ptr<CompressionStrategy> s) { strategy_ = std::move(s); }
};

// Usage — swap at runtime!
Compressor c(std::make_unique<ZipStrategy>());
c.compress("data");  // "zip:data"
c.setStrategy(std::make_unique<GzipStrategy>());
c.compress("data");  // "gzip:data"
interface CompressionStrategy {
    String compress(String data);
}

class ZipStrategy implements CompressionStrategy {
    public String compress(String data) { return "zip:" + data; }
}

class GzipStrategy implements CompressionStrategy {
    public String compress(String data) { return "gzip:" + data; }
}

public final class Compressor {
    private CompressionStrategy strategy;
    public Compressor(CompressionStrategy s) { this.strategy = s; }
    public String compress(String data) { return strategy.compress(data); }
    public void setStrategy(CompressionStrategy s) { this.strategy = s; }
}

// Usage — swap at runtime!
Compressor c = new Compressor(new ZipStrategy());
c.compress("data");  // "zip:data"
c.setStrategy(new GzipStrategy());
c.compress("data");  // "gzip:data"
public interface ICompressionStrategy {
    string Compress(string data);
}

public sealed class ZipStrategy : ICompressionStrategy {
    public string Compress(string data) => "zip:" + data;
}

public sealed class GzipStrategy : ICompressionStrategy {
    public string Compress(string data) => "gzip:" + data;
}

public sealed class Compressor {
    private ICompressionStrategy _strategy;
    public Compressor(ICompressionStrategy s) => _strategy = s;
    public string Compress(string data) => _strategy.Compress(data);
    public void SetStrategy(ICompressionStrategy s) => _strategy = s;
}

// Usage — swap at runtime!
var c = new Compressor(new ZipStrategy());
c.Compress("data");  // "zip:data"
c.SetStrategy(new GzipStrategy());
c.Compress("data");  // "gzip:data"