Strategy Design Pattern laravel php

Strategy Design Pattern consente di definire una famiglia di algoritmi, incapsulare ciascuno di essi e renderli intercambiabili.

Il codice client ( la classe principale ) sarà in grado di scegliere un algoritmo ( servizio ) in fase di esecuzione.

In questo articolo andremo ad approfondire, anche con esempi, lo Strategy Design Pattern Laravel PHP

Design Pattern Strategy

Il Design Pattern Strategy rientra nella categoria dei behavioral pattern e consente la selezione di algoritmi in fase di esecuzione. Ciò è particolarmente utile in situazioni in cui si desidera passare dinamicamente da un algoritmo all’altro o da un comportamento all’altro.

Nel contesto di Laravel/PHP, il pattern Strategy consente di disaccoppiare algoritmi specifici dal client, consentendo di passare facilmente da uno all’altro. Questo approccio aderisce al principio Open/Closed (uno dei principi SOLID ), consentendo al codice di essere aperto per l’estensione ma chiuso per la modifica.

Problema

In molte applicazioni Laravel, ci sono scenari in cui più algoritmi o comportamenti devono essere implementati in base alle condizioni di runtime. Ad esempio:
hai un’app in cui gli utenti possono registrarsi e utilizzare i tuoi servizi. Ora, ovviamente, vuoi proteggere la tua app da account falsi. Un modo per farlo è impedire la registrazione di:

  • Email false
  • Email temporanee o monouso
  • Email non valide ( email che sembrano valide ma in realtà non esistono ).

Per farlo possiamo creare un validatore che convalida le email monouso, e verificare anche se l’email è valida tramite un provider esterno.

Possiamo usare kickbox.com come nostro servizio, e tutto va bene.
Se poi volessimo implementare anche un altro servizio zerobounce.net, allora possiamo procedere a implementarlo.

Più avanti, supponiamo di valutare un’altra esigenza per supportare anche usercheck.com , e così via.

Grazie al pattern Strategy, possiamo gestire queste esigenze senza l’uso di istruzioni come if-else or switch.

Se invece usassimo una programmazione più strutturata, e quindi con l’uso pesante di if-else e switch, arriveremo ad avere un accoppiamento stretto del codice, che ci renderebbe molto difficile l’aggiunta o la modifica di algoritmi in una fase successiva. Il codice diventa complesso, più difficile da testare e viola i principi di progettazione chiave.

Soluzione

Il pattern Strategy offre una soluzione elegante a questo problema separando gli algoritmi in classi individuali che implementano un’interfaccia comune. L’idea di base è:

  • Definire un’interfaccia che verrà implementata da tutti gli algoritmi.
  • Implementare ciascun algoritmo nella sua classe concreta.
  • Una classe di contesto utilizza questi algoritmi e può passare dinamicamente da uno all’altro senza dover modificare la propria logica interna.

Ciò si traduce in un codice pulito e manutenibile , facile da estendere e modificare senza alterare la logica esistente. Basta aggiungere nuove strategie in base alle necessità, senza modificare il codice client.

Prima di addentrarci nell’implementazione, comprendiamo i componenti chiave che costituiscono il modello dell’osservatore.

  1. Contesto : l’oggetto che delegherà il suo comportamento a una delle strategie contenute.
  2. Interfaccia strategia : l’interfaccia che definisce il comportamento per tutte le strategie. Le strategie implementano questa interfaccia per fornire la loro implementazione unica del comportamento.
  3. Strategie concrete : le classi che implementano l’interfaccia della strategia. Ogni strategia incapsula un comportamento specifico a cui il contesto può passare in fase di esecuzione.

Codice

  1. Interfaccia strategica . Per prima cosa, creiamo un’interfaccia comune per diverse strategie di convalida.
<?php

interface EmailValidationServiceInterface
{
public function isRealEmail(string $email): bool;
}

2. Ora creiamo alcune strategie concrete per ogni servizio che vogliamo implementare: Kickbox, Zerobounce e Usercheck.

<?php

class KickboxService implements EmailValidationServiceInterface
{
public function isRealEmail(string $email): bool
{
// KickboxService specific logic
return true;
}
}

class ZeroBounceService implements EmailValidationServiceInterface
{
public function isRealEmail(string $email): bool
{
// ZeroBounceServicespecific logic
return true;
}
}

class UsercheckService implements EmailValidationServiceInterface
{
public function isRealEmail(string $email): bool
{
// UsercheckService specific logic
return true;
}
}

3. Ora dobbiamo implementare una classe di contesto . Questa è responsabile della selezione della strategia di servizio di convalida appropriata.

<?php

class EmailValidationContext
{
private EmailValidationServiceInterface $emailValidation;

public function setEmailValidator(EmailValidationServiceInterface $emailValidation): EmailValidationServiceInterface
{
$this->emailValidation = $emailValidation;
}

public function validate(string $email): bool
{
return $this->emailValidation->isRealEmail($email);
}
}

Utilizzo

L’ultima parte riguarda il modo in cui possiamo usare la strategia nella nostra applicazione. Possiamo implementare la strategia di convalida email direttamente nel nostro controller o in un validatore Laravel personalizzato.

<?php

class RegisterController extends Controller
{
public function check(Request $request)
{

$emailValidationContext = new EmailValidationContext();

// For the sake of argument lets suppose the service comes from request
// or even better is configured in `/config/services.php`


// Select the strategy based on the chosen service
switch ($request->service) {
case 'kickbox':
$emailValidationContext->setEmailValidator(new KickboxService());
break;
case 'zerobounce':
$emailValidationContext->setEmailValidator(new ZeroBounceService());
break;
case 'usercheck':
$emailValidationContext->setEmailValidator(new UsercheckService());
break;
default:
throw new \Exception("Validation service not supported.");
}


// validate email with the service
return $emailValidationContext->validate($request->email);
}
}

Benefici

1. Flessibilità ed estensibilità

Il pattern Strategy semplifica l’aggiunta di nuove strategie senza modificare il codice esistente. Ad esempio, se hai bisogno di aggiungere un nuovo servizio di validazione, crei semplicemente una nuova classe che implementa EmailValidationServiceInterface e la inietti nel contesto.

2. Aderenza ai principi SOLID

  • Principio aperto/chiuso : il sistema è aperto alle estensioni ma chiuso alle modifiche, consentendo di estendere le funzionalità senza alterare il codice esistente.
  • Principio di responsabilità unica : ogni classe ha una responsabilità, il che ne semplifica la manutenzione e il test.

3. Eliminazione della logica condizionale

Questo modello elimina la necessità di istruzioni complesse if-elseswitch rendendo il codice più pulito e leggibile.

Altri esempi

Puoi usare i modelli di progettazione Strategy anche per altri casi d’uso, come l’elaborazione di ordini con diversi gateway di pagamento in una piattaforma di e-commerce. Ad esempio, puoi avere PayPal, Stripe, BankTransfer, ecc., oppure usare diversi servizi di recapito e-mail, Mailgun, SendingBlue, SMTP, ecc.

Autore