
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

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
como decidimos que estrategia usar? o acaso hay que hacer un call a todas?
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!
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
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
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