23 Classic Patterns with Python Implementations
The 23 classic design patterns from "Design Patterns: Elements of Reusable Object-Oriented Software" by Gamma, Helm, Johnson, and Vlissides (1994).
Categories:
| Pattern | Category | Intent |
|---|---|---|
| Singleton | Creational | Ensure a class has only one instance |
| Factory Method | Creational | Define interface for creating objects |
| Abstract Factory | Creational | Create families of related objects |
| Builder | Creational | Construct complex objects step by step |
| Prototype | Creational | Clone existing objects |
| Adapter | Structural | Make incompatible interfaces work together |
| Bridge | Structural | Separate abstraction from implementation |
| Composite | Structural | Compose objects into tree structures |
| Decorator | Structural | Add behavior to objects dynamically |
| Facade | Structural | Simplified interface to complex subsystem |
| Flyweight | Structural | Share common state among many objects |
| Proxy | Structural | Provide substitute for another object |
| Chain of Responsibility | Behavioral | Pass request along chain of handlers |
| Command | Behavioral | Encapsulate request as object |
| Iterator | Behavioral | Access elements sequentially |
| Mediator | Behavioral | Reduce coupling via centralized communication |
| Memento | Behavioral | Save and restore object state |
| Observer | Behavioral | Subscription mechanism for notifications |
| State | Behavioral | Alter behavior when state changes |
| Strategy | Behavioral | Make algorithms interchangeable |
| Template Method | Behavioral | Define skeleton, override steps |
| Visitor | Behavioral | Separate algorithm from structure |
| Interpreter | Behavioral | Define grammar and interpreter |
Object Creation Mechanisms
import threading
class Singleton:
"""Singleton Pattern - Only one instance exists"""
_instance = None
_initialized = False
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
# Only initialize once
if not Singleton._initialized:
self.value = None
Singleton._initialized = True
def set_value(self, value):
self.value = value
def get_value(self):
return self.value
# Thread-safe Singleton (better for production)
class ThreadSafeSingleton:
_instance = None
_lock = threading.Lock()
def __new__(cls):
if cls._instance is None:
with cls._lock:
if cls._instance is None: # Double-checked locking
cls._instance = super().__new__(cls)
return cls._instance
# Usage
s1 = Singleton()
s1.set_value("Database Connection")
s2 = Singleton()
print(f"s1 value: {s1.get_value()}") # Database Connection
print(f"s2 value: {s2.get_value()}") # Database Connection
print(f"s1 is s2: {s1 is s2}") # True - same instance
from abc import ABC, abstractmethod
class Vehicle(ABC):
"""Abstract Product"""
@abstractmethod
def deliver(self):
pass
class Truck(Vehicle):
"""Concrete Product"""
def deliver(self):
return "Deliver by land in a truck"
class Ship(Vehicle):
"""Concrete Product"""
def deliver(self):
return "Deliver by sea in a ship"
class Logistics(ABC):
"""Creator - Factory Method Pattern"""
@abstractmethod
def create_transport(self) -> Vehicle:
"""Factory method - subclasses override this"""
pass
def plan_delivery(self):
transport = self.create_transport()
return transport.deliver()
class RoadLogistics(Logistics):
"""Concrete Creator"""
def create_transport(self) -> Vehicle:
return Truck()
class SeaLogistics(Logistics):
"""Concrete Creator"""
def create_transport(self) -> Vehicle:
return Ship()
# Usage
road = RoadLogistics()
print(road.plan_delivery()) # Deliver by land in a truck
sea = SeaLogistics()
print(sea.plan_delivery()) # Deliver by sea in a ship
from abc import ABC, abstractmethod
class Button(ABC):
"""Abstract Product A"""
@abstractmethod
def paint(self):
pass
class Checkbox(ABC):
"""Abstract Product B"""
@abstractmethod
def paint(self):
pass
class WindowsButton(Button):
def paint(self):
return "Render Windows button"
class MacButton(Button):
def paint(self):
return "Render Mac button"
class WindowsCheckbox(Checkbox):
def paint(self):
return "Render Windows checkbox"
class MacCheckbox(Checkbox):
def paint(self):
return "Render Mac checkbox"
class GUIFactory(ABC):
"""Abstract Factory"""
@abstractmethod
def create_button(self) -> Button:
pass
@abstractmethod
def create_checkbox(self) -> Checkbox:
pass
class WindowsFactory(GUIFactory):
def create_button(self) -> Button:
return WindowsButton()
def create_checkbox(self) -> Checkbox:
return WindowsCheckbox()
class MacFactory(GUIFactory):
def create_button(self) -> Button:
return MacButton()
def create_checkbox(self) -> Checkbox:
return MacCheckbox()
def render_ui(factory: GUIFactory):
button = factory.create_button()
checkbox = factory.create_checkbox()
print(f" {button.paint()}")
print(f" {checkbox.paint()}")
# Usage
print("Windows UI:")
render_ui(WindowsFactory())
print("Mac UI:")
render_ui(MacFactory())
class Pizza:
"""Complex Product"""
def __init__(self):
self.dough = None
self.sauce = None
self.toppings = []
def __str__(self):
return f"Pizza(dough={self.dough}, sauce={self.sauce}, toppings={self.toppings})"
class PizzaBuilder:
"""Builder Pattern"""
def __init__(self):
self.pizza = Pizza()
def set_dough(self, dough):
self.pizza.dough = dough
return self # Fluent interface
def set_sauce(self, sauce):
self.pizza.sauce = sauce
return self
def add_topping(self, topping):
self.pizza.toppings.append(topping)
return self
def build(self):
return self.pizza
# Director (optional) - encapsulates construction steps
class PizzaDirector:
def __init__(self, builder: PizzaBuilder):
self.builder = builder
def make_margherita(self):
return (self.builder
.set_dough("thin")
.set_sauce("tomato")
.add_topping("mozzarella")
.add_topping("basil")
.build())
def make_pepperoni(self):
return (self.builder
.set_dough("thick")
.set_sauce("tomato")
.add_topping("mozzarella")
.add_topping("pepperoni")
.build())
# Usage
# Direct building
custom_pizza = (PizzaBuilder()
.set_dough("regular")
.set_sauce("white")
.add_topping("chicken")
.add_topping("mushrooms")
.build())
print(f"Custom: {custom_pizza}")
# Using Director
director = PizzaDirector(PizzaBuilder())
margherita = director.make_margherita()
print(f"Margherita: {margherita}")
import copy
from abc import ABC, abstractmethod
class Prototype(ABC):
"""Prototype Pattern"""
@abstractmethod
def clone(self):
pass
class Document(Prototype):
def __init__(self, title, content, metadata=None):
self.title = title
self.content = content
self.metadata = metadata or {}
def clone(self):
# Shallow copy
return copy.copy(self)
def deep_clone(self):
# Deep copy (for nested objects)
return copy.deepcopy(self)
def __str__(self):
return f"Document('{self.title}', content_len={len(self.content)}, metadata={self.metadata})"
# Usage
original = Document("Template", "Lorem ipsum...", {"author": "Admin", "tags": ["template"]})
print(f"Original: {original}")
# Clone and modify
clone = original.clone()
clone.title = "New Document"
clone.metadata["author"] = "User" # Note: shallow copy shares metadata dict!
print(f"Clone: {clone}")
print(f"Original after clone modification: {original}") # Metadata changed!
# Deep clone to avoid shared references
deep_clone = original.deep_clone()
deep_clone.title = "Deep Clone"
deep_clone.metadata["author"] = "Another User"
print(f"Deep Clone: {deep_clone}")
print(f"Original after deep clone: {original}") # Metadata unchanged
Object Composition and Relationships
class EuropeanSocket:
"""Existing class with incompatible interface"""
def voltage(self):
return 230
def live(self):
return 1
def neutral(self):
return -1
class USASocket(ABC):
"""Target interface"""
@abstractmethod
def voltage(self):
pass
@abstractmethod
def live(self):
pass
@abstractmethod
def neutral(self):
pass
class Adapter(USASocket):
"""Adapter Pattern"""
def __init__(self, socket: EuropeanSocket):
self.socket = socket
def voltage(self):
return 110 # Convert 230V to 110V
def live(self):
return self.socket.live()
def neutral(self):
return self.socket.neutral()
# Usage
eu_socket = EuropeanSocket()
adapter = Adapter(eu_socket)
print(f"EU voltage: {eu_socket.voltage()}V") # 230V
print(f"Adapted voltage: {adapter.voltage()}V") # 110V
class Device(ABC):
"""Implementation interface"""
@abstractmethod
def is_enabled(self):
pass
@abstractmethod
def enable(self):
pass
@abstractmethod
def disable(self):
pass
@abstractmethod
def get_volume(self):
pass
@abstractmethod
def set_volume(self, percent):
pass
class TV(Device):
def __init__(self):
self._on = False
self._volume = 50
def is_enabled(self):
return self._on
def enable(self):
self._on = True
def disable(self):
self._on = False
def get_volume(self):
return self._volume
def set_volume(self, percent):
self._volume = percent
class RemoteControl:
"""Abstraction - Bridge Pattern"""
def __init__(self, device: Device):
self.device = device
def toggle_power(self):
if self.device.is_enabled():
self.device.disable()
else:
self.device.enable()
def volume_up(self):
self.device.set_volume(self.device.get_volume() + 10)
def volume_down(self):
self.device.set_volume(self.device.get_volume() - 10)
class AdvancedRemote(RemoteControl):
"""Extended abstraction"""
def mute(self):
self.device.set_volume(0)
# Usage
tv = TV()
remote = RemoteControl(tv)
remote.toggle_power()
remote.volume_up()
print(f"TV: On={tv.is_enabled()}, Volume={tv.get_volume()}")
class Graphic(ABC):
"""Component"""
@abstractmethod
def draw(self):
pass
class Circle(Graphic):
"""Leaf"""
def __init__(self, name):
self.name = name
def draw(self):
return f"Circle({self.name})"
class Square(Graphic):
"""Leaf"""
def __init__(self, name):
self.name = name
def draw(self):
return f"Square({self.name})"
class CompositeGraphic(Graphic):
"""Composite"""
def __init__(self, name):
self.name = name
self.children = []
def add(self, graphic: Graphic):
self.children.append(graphic)
def remove(self, graphic: Graphic):
self.children.remove(graphic)
def draw(self):
results = [f"Group({self.name}): ["]
for child in self.children:
results.append(f" {child.draw()}")
results.append("]")
return "\n".join(results)
# Usage - Build tree structure
all_graphics = CompositeGraphic("All")
group1 = CompositeGraphic("Group1")
group1.add(Circle("c1"))
group1.add(Square("s1"))
group2 = CompositeGraphic("Group2")
group2.add(Circle("c2"))
all_graphics.add(group1)
all_graphics.add(group2)
all_graphics.add(Circle("c3"))
print(all_graphics.draw())
class Coffee(ABC):
"""Component"""
@abstractmethod
def cost(self):
pass
@abstractmethod
def description(self):
pass
class SimpleCoffee(Coffee):
"""Concrete Component"""
def cost(self):
return 2.0
def description(self):
return "Simple coffee"
class CoffeeDecorator(Coffee):
"""Base Decorator"""
def __init__(self, coffee: Coffee):
self._coffee = coffee
@abstractmethod
def cost(self):
pass
@abstractmethod
def description(self):
pass
class Milk(CoffeeDecorator):
def cost(self):
return self._coffee.cost() + 0.5
def description(self):
return self._coffee.description() + ", milk"
class Sugar(CoffeeDecorator):
def cost(self):
return self._coffee.cost() + 0.2
def description(self):
return self._coffee.description() + ", sugar"
class Whip(CoffeeDecorator):
def cost(self):
return self._coffee.cost() + 0.7
def description(self):
return self._coffee.description() + ", whip"
# Usage - Build decorated object
coffee = SimpleCoffee()
print(f"{coffee.description()}: ${coffee.cost()}")
coffee_with_milk = Milk(coffee)
print(f"{coffee_with_milk.description()}: ${coffee_with_milk.cost()}")
coffee_deluxe = Whip(Sugar(Milk(SimpleCoffee())))
print(f"{coffee_deluxe.description()}: ${coffee_deluxe.cost()}")
class CPU:
"""Complex subsystem class"""
def freeze(self):
return "CPU frozen"
def jump(self, position):
return f"CPU jumping to {position}"
def execute(self):
return "CPU executing"
class Memory:
"""Complex subsystem class"""
def load(self, position, data):
return f"Memory loaded {data} at {position}"
class HardDrive:
"""Complex subsystem class"""
def read(self, lba, size):
return f"HardDrive read {size} bytes from {lba}"
class ComputerFacade:
"""Facade Pattern"""
def __init__(self):
self.cpu = CPU()
self.memory = Memory()
self.hard_drive = HardDrive()
def start(self):
"""Simple interface to complex boot sequence"""
results = []
results.append(self.cpu.freeze())
results.append(self.memory.load("0x00", "boot data"))
results.append(self.hard_drive.read("0", 1024))
results.append(self.cpu.jump("0x00"))
results.append(self.cpu.execute())
return " → ".join(results)
# Usage
computer = ComputerFacade()
print(computer.start())
class TreeType:
"""Flyweight - shared immutable state"""
def __init__(self, name, color, texture):
self.name = name
self.color = color
self.texture = texture
def draw(self, x, y):
return f"Draw {self.color} {self.name} at ({x},{y})"
class TreeFactory:
"""Flyweight Factory - manages shared objects"""
_tree_types = {}
@staticmethod
def get_tree_type(name, color, texture):
key = (name, color, texture)
if key not in TreeFactory._tree_types:
TreeFactory._tree_types[key] = TreeType(name, color, texture)
return TreeFactory._tree_types[key]
@staticmethod
def get_count():
return len(TreeFactory._tree_types)
class Tree:
"""Context object - extrinsic state"""
def __init__(self, x, y, tree_type: TreeType):
self.x = x
self.y = y
self.type = tree_type
def draw(self):
return self.type.draw(self.x, self.y)
# Usage - Create forest with many trees
forest = []
for i in range(1000):
# Only a few unique tree types (shared)
tree_type = TreeFactory.get_tree_type(
"Oak" if i % 2 == 0 else "Pine",
"Green",
"bark.jpg"
)
forest.append(Tree(i, i * 2, tree_type))
print(f"Created {len(forest)} trees")
print(f"Unique TreeType objects: {TreeFactory.get_count()}")
print(f"Memory saved: {len(forest) - TreeFactory.get_count()} object reuses")
class Image(ABC):
"""Subject"""
@abstractmethod
def display(self):
pass
class RealImage(Image):
"""Real Subject - expensive to create"""
def __init__(self, filename):
self.filename = filename
self._load_from_disk()
def _load_from_disk(self):
print(f"[RealImage] Loading {self.filename} from disk (expensive!)")
def display(self):
return f"Displaying {self.filename}"
class ImageProxy(Image):
"""Proxy Pattern - Virtual Proxy (lazy initialization)"""
def __init__(self, filename):
self.filename = filename
self._real_image = None
def display(self):
if self._real_image is None:
# Lazy initialization
self._real_image = RealImage(self.filename)
return self._real_image.display()
# Usage
print("Creating proxies (no loading yet):")
image1 = ImageProxy("photo1.jpg")
image2 = ImageProxy("photo2.jpg")
print("\nDisplaying image1 (loads now):")
print(image1.display())
print("\nDisplaying image1 again (already loaded):")
print(image1.display())
print("\nDisplaying image2 (loads now):")
print(image2.display())
Communication Between Objects
class Handler(ABC):
"""Handler interface"""
def __init__(self):
self._next = None
def set_next(self, handler):
self._next = handler
return handler
@abstractmethod
def handle(self, request):
pass
class AuthHandler(Handler):
def handle(self, request):
if "auth_token" not in request:
return "AuthHandler: Rejected - no auth token"
print("AuthHandler: Passed")
return self._next.handle(request) if self._next else "OK"
class ValidationHandler(Handler):
def handle(self, request):
if "data" not in request or not request["data"]:
return "ValidationHandler: Rejected - no data"
print("ValidationHandler: Passed")
return self._next.handle(request) if self._next else "OK"
class LoggingHandler(Handler):
def handle(self, request):
print(f"LoggingHandler: Logging request {request.get('id', 'unknown')}")
return self._next.handle(request) if self._next else "OK"
# Usage - Build chain
auth = AuthHandler()
validation = ValidationHandler()
logging = LoggingHandler()
auth.set_next(validation).set_next(logging)
# Test requests
print("Request 1 (valid):")
result = auth.handle({"id": 1, "auth_token": "abc", "data": "payload"})
print(f"Result: {result}\n")
print("Request 2 (no auth):")
result = auth.handle({"id": 2, "data": "payload"})
print(f"Result: {result}")
class Command(ABC):
"""Command interface"""
@abstractmethod
def execute(self):
pass
@abstractmethod
def undo(self):
pass
class Light:
"""Receiver"""
def __init__(self):
self.is_on = False
def turn_on(self):
self.is_on = True
return "Light is ON"
def turn_off(self):
self.is_on = False
return "Light is OFF"
class LightOnCommand(Command):
"""Concrete Command"""
def __init__(self, light: Light):
self.light = light
def execute(self):
return self.light.turn_on()
def undo(self):
return self.light.turn_off()
class LightOffCommand(Command):
def __init__(self, light: Light):
self.light = light
def execute(self):
return self.light.turn_off()
def undo(self):
return self.light.turn_on()
class RemoteControl:
"""Invoker"""
def __init__(self):
self.history = []
def submit(self, command: Command):
result = command.execute()
self.history.append(command)
return result
def undo(self):
if self.history:
command = self.history.pop()
return command.undo()
return "Nothing to undo"
# Usage
light = Light()
remote = RemoteControl()
print(remote.submit(LightOnCommand(light)))
print(f"Light state: {light.is_on}")
print(remote.submit(LightOffCommand(light)))
print(f"Light state: {light.is_on}")
print(remote.undo())
print(f"Light state after undo: {light.is_on}")
class Book:
def __init__(self, title):
self.title = title
class BookCollection:
"""Iterator Pattern"""
def __init__(self):
self._books = []
def add_book(self, book: Book):
self._books.append(book)
def __iter__(self):
"""Python's iterator protocol"""
return iter(self._books)
# Could also implement custom iterator
def reverse_iterator(self):
return reversed(self._books)
# Usage
collection = BookCollection()
collection.add_book(Book("Design Patterns"))
collection.add_book(Book("Clean Code"))
collection.add_book(Book("Refactoring"))
print("Forward iteration:")
for book in collection:
print(f" {book.title}")
print("Reverse iteration:")
for book in collection.reverse_iterator():
print(f" {book.title}")
class Mediator(ABC):
"""Mediator interface"""
@abstractmethod
def notify(self, sender, event):
pass
class ChatRoomMediator(Mediator):
"""Concrete Mediator"""
def __init__(self):
self.users = []
def register_user(self, user):
self.users.append(user)
def notify(self, sender, message):
for user in self.users:
if user != sender:
user.receive(f"{sender.name}: {message}")
class User:
"""Colleague"""
def __init__(self, name, mediator: ChatRoomMediator):
self.name = name
self.mediator = mediator
self.mediator.register_user(self)
def send(self, message):
print(f"{self.name} sends: {message}")
self.mediator.notify(self, message)
def receive(self, message):
print(f"{self.name} receives: {message}")
# Usage
chat_room = ChatRoomMediator()
alice = User("Alice", chat_room)
bob = User("Bob", chat_room)
charlie = User("Charlie", chat_room)
alice.send("Hello everyone!")
class EditorMemento:
"""Memento - stores state"""
def __init__(self, content, cursor):
self._content = content
self._cursor = cursor
def get_content(self):
return self._content
def get_cursor(self):
return self._cursor
class Editor:
"""Originator"""
def __init__(self):
self._content = ""
self._cursor = 0
def type(self, text):
self._content += text
self._cursor = len(self._content)
def save(self):
"""Create memento"""
return EditorMemento(self._content, self._cursor)
def restore(self, memento: EditorMemento):
"""Restore from memento"""
self._content = memento.get_content()
self._cursor = memento.get_cursor()
def get_content(self):
return self._content
class History:
"""Caretaker - manages mementos"""
def __init__(self):
self._mementos = []
def push(self, memento):
self._mementos.append(memento)
def pop(self):
if self._mementos:
return self._mementos.pop()
return None
# Usage
editor = Editor()
history = History()
editor.type("Hello ")
history.push(editor.save())
print(f"Content: '{editor.get_content()}'")
editor.type("World")
history.push(editor.save())
print(f"Content: '{editor.get_content()}'")
editor.type("!!!")
print(f"Content: '{editor.get_content()}'")
# Undo
memento = history.pop()
if memento:
editor.restore(memento)
print(f"After undo: '{editor.get_content()}'")
class Subject:
"""Subject (Observable)"""
def __init__(self):
self._observers = []
self._state = None
def attach(self, observer):
self._observers.append(observer)
def detach(self, observer):
self._observers.remove(observer)
def notify(self):
for observer in self._observers:
observer.update(self)
def set_state(self, state):
self._state = state
self.notify()
def get_state(self):
return self._state
class Observer(ABC):
"""Observer interface"""
@abstractmethod
def update(self, subject: Subject):
pass
class ConcreteObserverA(Observer):
def update(self, subject: Subject):
print(f"ObserverA: Reacted to state change: {subject.get_state()}")
class ConcreteObserverB(Observer):
def update(self, subject: Subject):
print(f"ObserverB: Reacted to state change: {subject.get_state()}")
# Usage
subject = Subject()
observer_a = ConcreteObserverA()
observer_b = ConcreteObserverB()
subject.attach(observer_a)
subject.attach(observer_b)
print("Setting state to 123:")
subject.set_state(123)
print("\nDetaching ObserverA")
subject.detach(observer_a)
print("Setting state to 456:")
subject.set_state(456)
class State(ABC):
"""State interface"""
@abstractmethod
def handle(self, context):
pass
class Context:
"""Context"""
def __init__(self, state: State):
self._state = state
def set_state(self, state: State):
print(f"Context: Transitioning to {state.__class__.__name__}")
self._state = state
def request(self):
self._state.handle(self)
class StartState(State):
def handle(self, context: Context):
print("StartState: Handling request")
context.set_state(RunningState())
class RunningState(State):
def handle(self, context: Context):
print("RunningState: Handling request")
context.set_state(StopState())
class StopState(State):
def handle(self, context: Context):
print("StopState: Handling request (no transition)")
# Usage
context = Context(StartState())
context.request()
context.request()
context.request()
class PaymentStrategy(ABC):
"""Strategy interface"""
@abstractmethod
def pay(self, amount):
pass
class CreditCardStrategy(PaymentStrategy):
def __init__(self, card_number):
self.card_number = card_number
def pay(self, amount):
return f"Paid ${amount} using Credit Card {self.card_number}"
class PayPalStrategy(PaymentStrategy):
def __init__(self, email):
self.email = email
def pay(self, amount):
return f"Paid ${amount} using PayPal {self.email}"
class BitcoinStrategy(PaymentStrategy):
def __init__(self, wallet):
self.wallet = wallet
def pay(self, amount):
return f"Paid ${amount} using Bitcoin {self.wallet}"
class ShoppingCart:
"""Context"""
def __init__(self, strategy: PaymentStrategy):
self._strategy = strategy
def set_strategy(self, strategy: PaymentStrategy):
self._strategy = strategy
def checkout(self, amount):
return self._strategy.pay(amount)
# Usage
cart = ShoppingCart(CreditCardStrategy("1234-5678"))
print(cart.checkout(100))
cart.set_strategy(PayPalStrategy("user@example.com"))
print(cart.checkout(50))
cart.set_strategy(BitcoinStrategy("1A2B3C..."))
print(cart.checkout(200))
class DataMiner(ABC):
"""Template Method Pattern"""
def mine(self, path):
"""Template method - defines skeleton"""
data = self.open_file(path)
raw_data = self.extract_data(data)
analyzed = self.analyze_data(raw_data)
self.send_report(analyzed)
self.close_file(data)
@abstractmethod
def open_file(self, path):
pass
@abstractmethod
def extract_data(self, file):
pass
def analyze_data(self, data):
"""Hook - can be overridden"""
return f"Analyzed: {data}"
@abstractmethod
def send_report(self, analysis):
pass
def close_file(self, file):
"""Hook"""
return "File closed"
class CSVDataMiner(DataMiner):
def open_file(self, path):
return f"CSV file opened: {path}"
def extract_data(self, file):
return "CSV data extracted"
def send_report(self, analysis):
print(f"CSV Report: {analysis}")
class JSONDataMiner(DataMiner):
def open_file(self, path):
return f"JSON file opened: {path}"
def extract_data(self, file):
return "JSON data extracted"
def analyze_data(self, data):
# Custom analysis
return f"JSON-specific analysis: {data}"
def send_report(self, analysis):
print(f"JSON Report: {analysis}")
# Usage
print("CSV Mining:")
csv_miner = CSVDataMiner()
csv_miner.mine("data.csv")
print("\nJSON Mining:")
json_miner = JSONDataMiner()
json_miner.mine("data.json")
class Shape(ABC):
"""Element"""
@abstractmethod
def accept(self, visitor):
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def accept(self, visitor):
visitor.visit_circle(self)
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def accept(self, visitor):
visitor.visit_rectangle(self)
class ShapeVisitor(ABC):
"""Visitor"""
@abstractmethod
def visit_circle(self, circle: Circle):
pass
@abstractmethod
def visit_rectangle(self, rectangle: Rectangle):
pass
class AreaCalculator(ShapeVisitor):
def visit_circle(self, circle: Circle):
area = 3.14 * circle.radius ** 2
print(f"Circle area: {area:.2f}")
def visit_rectangle(self, rectangle: Rectangle):
area = rectangle.width * rectangle.height
print(f"Rectangle area: {area:.2f}")
class PerimeterCalculator(ShapeVisitor):
def visit_circle(self, circle: Circle):
perimeter = 2 * 3.14 * circle.radius
print(f"Circle perimeter: {perimeter:.2f}")
def visit_rectangle(self, rectangle: Rectangle):
perimeter = 2 * (rectangle.width + rectangle.height)
print(f"Rectangle perimeter: {perimeter:.2f}")
# Usage
shapes = [Circle(5), Rectangle(4, 6)]
print("Calculating areas:")
area_calc = AreaCalculator()
for shape in shapes:
shape.accept(area_calc)
print("\nCalculating perimeters:")
perim_calc = PerimeterCalculator()
for shape in shapes:
shape.accept(perim_calc)
class Expression(ABC):
"""Interpreter Pattern"""
@abstractmethod
def interpret(self, context):
pass
class Number(Expression):
def __init__(self, value):
self.value = value
def interpret(self, context):
return self.value
class Add(Expression):
def __init__(self, left: Expression, right: Expression):
self.left = left
self.right = right
def interpret(self, context):
return self.left.interpret(context) + self.right.interpret(context)
class Subtract(Expression):
def __init__(self, left: Expression, right: Expression):
self.left = left
self.right = right
def interpret(self, context):
return self.left.interpret(context) - self.right.interpret(context)
# Usage - Represent: (5 + 3) - 2
expression = Subtract(
Add(Number(5), Number(3)),
Number(2)
)
result = expression.interpret({})
print(f"(5 + 3) - 2 = {result}")
Be ready to code 2-3 patterns from scratch!