SOLID è un insieme di principi per lo sviluppo di software orientato agli oggetti.
Questi principi contribuiscono a garantire che il software sia manutenibile, riutilizzabile ed estensibile.
In Questo articolo andremo ad approfondire l’applicazione dei principi SOLID in Laravel
Tempo di lettura: 6 minuti
I principi SOLID sono:
- Principio di responsabilità unica (SRP)
- Principio aperto-chiuso (OCP)
- Principio di sostituzione di Liskov (LSP)
- Principio di segregazione dell’interfaccia (ISP)
- Principio di inversione della dipendenza (DIP)
Come implementare principi SOLID in Laravel
- Single Responsibility Principle (SRP) Questo principio afferma che una classe dovrebbe avere una sola ragione per cambiare. In altre parole, una classe dovrebbe avere una sola responsabilità.
- Esempio: considera una classe chiamata
Order. Invece di avere tutte le funzionalità relative a un ordine in una classe, possiamo suddividerla in classi più piccole comeOrderCalculator,OrderRepository, eOrderMailer.
- Esempio: considera una classe chiamata
- Open-Closed Principle (OCP) Questo principio afferma che una classe dovrebbe essere aperta all’estensione ma chiusa alla modifica. In altre parole, dovremmo essere in grado di aggiungere nuove funzionalità senza modificare il codice esistente.
- Esempio: considera una classe chiamata
PaymentGateway. Invece di modificare questa classe ogni volta che aggiungiamo un nuovo metodo di pagamento, possiamo creare una nuova classe per ogni metodo di pagamento che estende laPaymentGatewayclasse.
- Esempio: considera una classe chiamata
- Principio di sostituzione di Liskov (LSP): questo principio stabilisce che gli oggetti di una superclasse devono poter essere sostituiti con oggetti di una sottoclasse senza compromettere la correttezza del programma.
- Esempio: considera una classe chiamata
Shapecon sottoclassiCircle,Rectangle, eSquare. Se abbiamo una funzione che accetta un oggetto di tipoShape, dovremmo essere in grado di passare oggetti di tipoCircle,Rectangle, oSquaresenza influenzare il comportamento della funzione.
- Esempio: considera una classe chiamata
- Principio di segregazione dell’interfaccia (ISP): questo principio stabilisce che i client non devono essere costretti a dipendere da metodi che non utilizzano.
- Esempio: considera un’interfaccia chiamata
PaymentMethodcon metodi comepay,refund, egetTransactions. Invece di avere tutti questi metodi in un’unica interfaccia, possiamo creare interfacce separate per ogni metodo e far sì che le classi implementino solo le interfacce di cui hanno bisogno.
- Esempio: considera un’interfaccia chiamata
Principio di inversione delle dipendenze(DIP) : Questo principio afferma che i moduli di alto livello non dovrebbero dipendere dai moduli di basso livello. Entrambi dovrebbero dipendere dalle astrazioni.- Esempio: considera una classe chiamata
Orderche dipende da una classe chiamataOrderRepository. Invece di istanziare direttamenteOrderRepositoryinOrder, possiamo usare l’iniezione di dipendenza per iniettare un’istanza diOrderRepositoryinOrder.
- Esempio: considera una classe chiamata
Se sei un freelance specializzato in sviluppo software, su freelancewww.it ci sono molti progetti che ti aspettano
Vantaggi
Seguendo questi principi SOLID in Laravel, possiamo scrivere codice pulito, manutenibile ed estensibile.
Per illustrare ulteriormente come raggiungere i principi SOLID in Laravel, diamo un’occhiata a un esempio pratico.
Supponiamo di avere un’applicazione che consente agli utenti di effettuare ordini di prodotti. Vogliamo implementare la funzionalità per calcolare il prezzo totale di un ordine e inviare un’e-mail al cliente con i dettagli dell’ordine. Possiamo ottenere questo risultato seguendo i principi SOLID.
Principio di responsabilità unica (SRP)
Per seguire SRP, possiamo creare classi separate per calcolare il prezzo totale di un ordine e inviare un’e-mail al cliente. Ad esempio:
class OrderCalculator
{
public function calculateTotal(Order $order): float
{
// Calculate total price of order
}
}
class OrderMailer
{
public function sendEmail(Order $order, User $user)
{
// Send email to customer with order details
}
}
Principio aperto-chiuso (OCP)
Per seguire OCP, possiamo usare il service container e l’iniezione di dipendenza di Laravel per creare un sistema flessibile che ci consente di aggiungere nuove funzionalità senza modificare il codice esistente. Ad esempio:
interface PaymentGateway
{
public function pay(Order $order): bool;
}
class PayPalGateway implements PaymentGateway
{
public function pay(Order $order): bool
{
// Process payment using PayPal API
}
}
class StripeGateway implements PaymentGateway
{
public function pay(Order $order): bool
{
// Process payment using Stripe API
}
}
class OrderProcessor
{
private PaymentGateway $gateway;
public function __construct(PaymentGateway $gateway)
{
$this->gateway = $gateway;
}
public function process(Order $order): void
{
// Process order using PaymentGateway
}
}
// In application service provider
$this->app->bind(PaymentGateway::class, StripeGateway::class);
Qui, abbiamo creato PaymentGateway un’interfaccia e due implementazioni per PayPal e Stripe. Creiamo quindi una classe OrderProcessor che prende un’istanza PaymentGateway tramite il suo costruttore. Nel provider di servizi applicativi, associamo l’interfaccia PaymentGateway all’implementazione StripeGateway, ma possiamo facilmente modificarla per usare l’implementazione PayPalGateway se necessario.
Potrebbe interessarti anche Che cos’è un webhook e come si usa?
Principio di sostituzione di Liskov (LSP)
Per seguire LSP, il terzo dei principi solid, dobbiamo assicurarci che qualsiasi sottoclasse di una superclasse possa essere usata al posto della superclasse senza influenzare il comportamento del programma. Nel nostro esempio, possiamo assicurarci di ciò usando suggerimenti sui tipi e interfacce. Ad esempio:
interface OrderRepository
{
public function save(Order $order): void;
}
class DatabaseOrderRepository implements OrderRepository
{
public function save(Order $order): void
{
// Save order to database
}
}
class InMemoryOrderRepository implements OrderRepository
{
public function save(Order $order): void
{
// Save order to in-memory cache
}
}
class OrderService
{
private OrderRepository $repository;
public function __construct(OrderRepository $repository)
{
$this->repository = $repository;
}
public function placeOrder(Order $order): void
{
// Place order and save to repository
}
}
// In application service provider
$this->app->bind(OrderRepository::class, DatabaseOrderRepository::class);
Qui, abbiamo creato un’interfaccia OrderRepository e due implementazioni per un database e una cache in memoria. Creiamo quindi una classe OrderService che accetta un’istanza OrderRepository tramite il suo costruttore. Nel provider di servizi applicativi, associamo l’interfaccia OrderRepository all’implementazione DatabaseOrderRepository, ma possiamo facilmente modificarla per usare l’implementazione InMemoryOrderRepository se necessario, senza influenzare il comportamento del programma.
Principio di segregazione dell’interfaccia (ISP)
Per seguire l’ISP, il quarto dei principi solid, non dovremmo forzare i client a dipendere da interfacce che non usano. Nel nostro esempio, possiamo assicurarci di ciò creando interfacce più piccole e più mirate invece di interfacce grandi e monolitiche. Ad esempio:
interface OrderTotalCalculator
{
public function calculateTotal(Order $order): float;
}
interface OrderEmailSender
{
public function sendEmail(Order $order, User $user);
}
class OrderProcessor
{
private OrderTotalCalculator $calculator;
private OrderEmailSender $mailer;
public function __construct(OrderTotalCalculator $calculator, OrderEmailSender $mailer)
{
$this->calculator = $calculator;
$this->mailer = $mailer;
}
public function process(Order $order, User $user): void
{
$total = $this->calculator->calculateTotal($order);
$this->mailer->sendEmail($order, $user);
// Process order using total and mailer
}
}
Qui, abbiamo creato due interfacce più piccole per calcolare il prezzo totale di un ordine e inviare un’e-mail, rispettivamente. Modifichiamo quindi la classe OrderProcessor per prendere istanze di queste interfacce invece di una singola, grande interfaccia. Ciò consente ai client di dipendere solo dalle interfacce di cui hanno bisogno, anziché essere costretti a dipendere da una grande interfaccia monolitica.
Principio di inversione della dipendenza (DIP)
Per seguire DIP, dovremmo affidarci ad astrazioni invece che a implementazioni concrete. Nel nostro esempio, possiamo ottenere questo risultato utilizzando l’iniezione di dipendenza e le interfacce in tutto il nostro codice. Ad esempio:
interface PaymentGateway
{
public function pay(Order $order): bool;
}
interface OrderRepository
{
public function save(Order $order): void;
}
interface OrderTotalCalculator
{
public function calculateTotal(Order $order): float;
}
interface OrderEmailSender
{
public function sendEmail(Order $order, User $user);
}
class OrderProcessor
{
private PaymentGateway $gateway;
private OrderRepository $repository;
private OrderTotalCalculator $calculator;
private OrderEmailSender $mailer;
public function __construct(
PaymentGateway $gateway,
OrderRepository $repository,
OrderTotalCalculator $calculator,
OrderEmailSender $mailer
) {
$this->gateway = $gateway;
$this->repository = $repository;
$this->calculator = $calculator;
$this->mailer = $mailer;
}
public function process(Order $order, User $user): void
{
$total = $this->calculator->calculateTotal($order);
$this->mailer->sendEmail($order, $user);
$this->gateway->pay($order);
$this->repository->save($order);
// Process order using gateway, repository, calculator, and mailer
}
}
Qui abbiamo creato interfacce per tutte le nostre dipendenze e modificato la classe OrderProcessor per prendere istanze di queste interfacce tramite il suo costruttore. Questo ci consente di scambiare facilmente le implementazioni in fase di esecuzione e ci consente di dipendere da astrazioni anziché da implementazioni concrete.
In sintesi, possiamo raggiungere i principi SOLID in Laravel utilizzando l’iniezione di dipendenza, le interfacce e il contenitore di servizi Laravel per creare un sistema flessibile e manutenibile, facile da modificare ed estendere.
Letture Correlate
Ercole Palmeri

