Strategy Pattern en acción con PHP


En el artículo anterior ya hemos visto la definición del Strategy Patern y como se aplica, ahora vamos a ver un ejemplo de la vida real, para eso vamos implementar los métodos de pago que podría tener nuestra tienda virtual.

Contexto

Nuestra pequeña tienda virtual permite que nuestros clientes paguen con tarjeta de crédito, pero como el negocio va creciendo tenemos que adaptarnos al entorno, así que tenemos que implementar otras formas adicionales de pagos como: deposito en cuenta, pago a través de teléfonos móviles, pago contra entrega, cheque, etc.

Cada método de pago tiene una forma distinta de implementarse, ya que cada uno requiere y maneja datos e información distinta, entonces debemos encapsular cada uno de ellos.

Para este ejemplo vamos a implementar el pago con tarjeta de crédito y con PayPal

UML

shoping_cart

Implementación

En primer lugar vamos a crear la interfaz de nuestra estrategia, en nuestro caso, la cantidad que se va pagar lo vamos a pasar como argumento.

// PaymentInterface.php
namespace Behavioral\Strategy\Example\ShoppingCart;

interface PaymentInterface
{
    public function pay($amount);
}

Ahora tendremos que crear las estrategias concretas (algoritmos) para el pago mediante tarjeta de crédito y PayPal.

//CreditCardPayment.php
namespace Behavioral\Strategy\Example\ShoppingCart;

use Behavioral\Strategy\Example\ShoppingCart\PaymentInterface;

class CreditCardPayment implements PaymentInterface
{

    private $name;
    private $cardNumber;
    private $cvv;
    private $dateOfExpiry;

    public function __construct($name, $cardNumber, $cvv, $dateOfExpiry)
    {
        $this->name         = $name;
        $this->cardNumber   = $cardNumber;
        $this->cvv          = $cvv;
        $this->dateOfExpiry = $dateOfExpiry;
    }

    public function pay($amount)
    {
        echo $amount." paid with credit/debit card";
    }
}
//PaypalPayment.php
namespace Behavioral\Strategy\Example\ShoppingCart;

use Behavioral\Strategy\Example\ShoppingCart\PaymentInterface;

class PaypalPayment implements PaymentInterface
{

    private $emailId;
    private $password;

    public function __construct($email, $password)
    {
        $this->emailId              = $email;
        $this->password = $password;
    }

    public function pay($amount)
    {
        echo $amount." paid using Paypal";
    }
}

Ahora nuestras estrategias (algoritmos) están listos y podemos implementar el Carrito de Compras. Observa que el método de pago del Carrito de Compras, requiere el algoritmo de pago como argumento

//ShoppingCart.php
namespace Behavioral\Strategy\Example\ShoppingCart;

use Behavioral\Strategy\Example\ShoppingCart\Item;
use Behavioral\Strategy\Example\ShoppingCart\PaymentInterface;

class ShoppingCart
{

    private $items = [];

    public function addItem(Item $item)
    {
        $this->items[$item->getId()] = $item;
    }

    public function removeItem($itemId)
    {
        if (isset($this->items[$itemId])) {
            unset($this->items[$itemId]);
        }
    }

    public function getItems()
    {
        return $this->items;
    }

    public function calculateTotal()
    {
        $sum = 0;
        foreach ($this->items as $items) {
            $sum += $items->getPrice();
        }
        return $sum;
    }

    public function pay(PaymentInterface $paymentMethod)
    {
        $amount = $this->calculateTotal();
        $paymentMethod->pay($amount);
    }
}

Crearemos una clase Item para almacenar la información de los productos y pasárselo al carrito de compra

//Item.php
namespace Behavioral\Strategy\Example\ShoppingCart;

class Item
{

    private $id;
    private $upcCode;
    private $price;

    public function __construct($id, $upc, $cost)
    {
        $this->id      = $id;
        $this->upcCode = $upc;
        $this->price   = $cost;
    }

    public function getId()
    {
        return $this->id;
    }

    public function getUpcCode()
    {
        return $this->upcCode;
    }

    public function getPrice()
    {
        return $this->price;
    }
}

Vamos a probar como funciona nuestra programa

use Behavioral\Strategy\Example\ShoppingCart\Item;
use Behavioral\Strategy\Example\ShoppingCart\ShoppingCart;
use Behavioral\Strategy\Example\ShoppingCart\CreditCardPayment;
use Behavioral\Strategy\Example\ShoppingCart\PaypalPayment;

$cart = new ShoppingCart();

$item1 = new Item(1, '124', 10);
$item2 = new Item(2, '14', 40);

$cart->addItem($item1);
$cart->addItem($item2);

/** Si mi cliente va usar una tarjeta de crédito, uso esto* */
//Pago con Tarjeta de Crédito
$cart->pay(new CreditCardPayment("Jhon W.", "101012123456", "786", "12/15"));
//El resultado sera:
//50 paid with credit/debit card

/** Si mi cliente va usar su cuenta paypal uso esto* */
//Pago con Paypal
$cart->pay(new PaypalPayment("my_email@example.com", "my_password"));
//El resultado sera:
//50 paid using Paypal

De esa manera podemos agregar muchas mas formas de pago sin necesidad de tocar las demás clases haciendo mas flexible nuestro programa.

¿Sencillo no?

Los archivos lo puedes encontrar en en el siguiente enlace:
https://github.com/benjamingb/DesignPatternsPHP/tree/master/Behavioral/Strategy

benjamin
Me llamo Benjamín Gonzales B, soy desarrollador de software con más de 15 años de experiencia, socio funduador de la empresa GNBIT. Me apasiona todo lo relacionado a las nuevas tecnologías, me gusta investigar , leer y aprender cada día algo nuevo. Desarrollo en PHP7+, JAVA, C#, JavaScript, entre otros y actualmente  estoy experimentando con lenguajes funcionales como: Erlang, Clojure y Scala 

6 Comments

    1. Hola Jorge
      La estrategia que vas usar es según tu contexto, esto quiere decir que si viene un cliente a tu tienda virtual y desea pagar con una tarjeta de crédito (contexto) ya tienes preparada un algoritmo que procesa la tarjeta de crédito (estrategia), si otro cliente desea pagar con un cheque ya tienes preparada otro algoritmo para procesar un cheque.

      Aquí hay una explicación mas amplia sobre este patrón Strategy Pattern

      Recuerda que por lo general es una estrategia para un contexto, cambia de contexto cambia de estrategia.

      Espero haberte ayudado

      Saludos!

    2. Creo que la pregunta de Jorge es , porque no usatse un if o switch para llamar a $cart->pay

      Si no que llamste a ambas funciones sin cambiar o filtrar el contexto

      1. Hola Daniel
        El objetivo del ejemplo era mostrar las formas distintas de llamarlo, y eso lo puse en los comentarios, pero tienes razón, creo que poniendo un if hubiese quedado mas claro.
        Gracias

  1. El ejemplo tira mas para FactoryMethod que Strategy. Hubieras puesto como ejemplo:

    – Si el monto a pagar es mayor a X, usar tarjeta, si no, cambias de estrategia y pagas con paypal

Leave a Comment

Su dirección de correo no se hará público. Los campos requeridos están marcados *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.