"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.
:::
Si occupano della creazione degli oggetti, cercando di separare il sistema dai dettagli di come i suoi oggetti vengono creati e composti.
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...")
Riguardano la composizione di classi e oggetti per formare strutture più grandi e flessibili.
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;
}
};
Si concentrano sulla comunicazione tra oggetti, ovvero come gli oggetti interagiscono e si distribuiscono le responsabilità.
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)
L'errore più comune di un ingegnere è l'Over-Engineering.
Ultimo aggiornamento: {{UPDATE_DATE}} | Tags: #SoftwareArchitecture #DesignPatterns #CleanCode #Python #CPP