Design Patterns: Architetture per il Codice

🏗️ Design Patterns: Architetture per il Codice

“I pattern sono soluzioni standard a problemi comuni nella progettazione del software.”

::: info I Design Patterns, formalizzati dalla Gang of Four (GoF), si dividono in tre categorie principali. Non sono snippet di codice da copiare, ma schemi mentali per strutturare la logica del software in modo che sia robusto e manutenibile. :::

1. Patterns Creazionali

Si occupano della creazione degli oggetti, cercando di separare il sistema dai dettagli di come i suoi oggetti vengono creati e composti.

Singleton (Python)

Garantisce che una classe abbia una sola istanza e fornisce un punto di accesso globale ad essa. Utilissimo per gestire pool di connessioni ai database o configurazioni.

from typing import Any

class SingletonMeta(type):
    """Metaclass that creates a Singleton instance."""
    _instances: dict[Any, Any] = {}

    def __call__(cls, *args: Any, **kwargs: Any) -> Any:
        if cls not in cls._instances:
            instance = super().__call__(*args, **kwargs)
            cls._instances[cls] = instance
        return cls._instances[cls]

class DatabaseConnection(metaclass=SingletonMeta):
    def connect(self) -> None:
        print("Connecting to Database...")

2. Patterns Strutturali

Riguardano la composizione di classi e oggetti per formare strutture più grandi e flessibili.

Adapter (C++)

Permette a interfacce incompatibili di lavorare insieme. Funge da “traduttore”.

#include <iostream>
#include <memory>
#include <string>

// Target interface
class ModernSensor {
public:
    virtual ~ModernSensor() = default;
    virtual float getTemperatureCelsius() const = 0;
};

// Legacy class with incompatible interface
class LegacyFahrenheitSensor {
public:
    float getTempF() const { return 98.6f; }
};

// Adapter: adapts Legacy to Modern
class SensorAdapter : public ModernSensor {
private:
    std::unique_ptr<LegacyFahrenheitSensor> legacy_sensor;

public:
    SensorAdapter() : legacy_sensor(std::make_unique<LegacyFahrenheitSensor>()) {}

    float getTemperatureCelsius() const override {
        return (legacy_sensor->getTempF() - 32.0f) * 5.0f / 9.0f;
    }
};

3. Patterns Comportamentali

Si concentrano sulla comunicazione tra oggetti, ovvero come gli oggetti interagiscono e si distribuiscono le responsabilità.

Strategy (Python)

Permette di definire una famiglia di algoritmi, incapsularli e renderli intercambiabili a runtime.

from abc import ABC, abstractmethod
from typing import List

class CompressionStrategy(ABC):
    @abstractmethod
    def compress(self, files: List[str]) -> None:
        pass

class ZIPCompression(CompressionStrategy):
    def compress(self, files: List[str]) -> None:
        print(f"Compressing {len(files)} files using ZIP.")

class RARCompression(CompressionStrategy):
    def compress(self, files: List[str]) -> None:
        print(f"Compressing {len(files)} files using RAR.")

class ArchiveManager:
    def __init__(self, strategy: CompressionStrategy) -> None:
        self._strategy = strategy

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

    def create_archive(self, files: List[str]) -> None:
        self._strategy.compress(files)

⚖️ Critical Spirit: Quando NON usarli

L’errore più comune di un ingegnere è l’Over-Engineering.

  • Non forzare i pattern: Se una funzione semplice risolve il problema, non creare una gerarchia di classi con Factory e Strategy.
  • YAGNI (You Ain’t Gonna Need It): Applica un pattern solo quando la complessità del problema lo richiede realmente, non “in previsione di” ipotetici cambiamenti futuri che potrebbero non avvenire mai.

Ultimo aggiornamento: {{UPDATE_DATE}} | Tags: #SoftwareArchitecture #DesignPatterns #CleanCode #Python #CPP

Last updated on Wednesday, February 18, 2026
Built with Hugo
Theme Stack designed by Jimmy