Menu
Coddy logo textTech

Patrón Strategy

Parte de la sección Programación Orientada a Objetos del Journey de PHP de Coddy — lección 74 de 91.

El Patrón Strategy es un patrón de diseño de comportamiento que permite definir una familia de algoritmos, encapsular cada uno en su propia clase y hacerlos intercambiables en tiempo de ejecución. El objeto que utiliza la estrategia no necesita saber qué algoritmo está ejecutando.

Imagina un carrito de compras que puede aplicar diferentes estrategias de descuento. En lugar de codificar de forma rígida la lógica de descuento con condicionales, defines cada descuento como una clase separada:

<?php
interface DiscountStrategy {
    public function calculate(float $total): float;
}

class NoDiscount implements DiscountStrategy {
    public function calculate(float $total): float {
        return $total;
    }
}

class PercentageDiscount implements DiscountStrategy {
    public function __construct(private int $percent) {}
    
    public function calculate(float $total): float {
        return $total - ($total * $this->percent / 100);
    }
}

class FixedDiscount implements DiscountStrategy {
    public function __construct(private float $amount) {}
    
    public function calculate(float $total): float {
        return max(0, $total - $this->amount);
    }
}

La clase de contexto acepta cualquier estrategia y delega el cálculo:

<?php
class ShoppingCart {
    public function __construct(private DiscountStrategy $discount) {}
    
    public function setDiscount(DiscountStrategy $discount): void {
        $this->discount = $discount;
    }
    
    public function checkout(float $total): float {
        return $this->discount->calculate($total);
    }
}

$cart = new ShoppingCart(new NoDiscount());
echo $cart->checkout(100) . "\n";

$cart->setDiscount(new PercentageDiscount(20));
echo $cart->checkout(100);

Salida:

100
80

El patrón Strategy destaca cuando tienes múltiples formas de realizar una operación y quieres cambiar entre ellas sin modificar la clase que las utiliza. Añadir un nuevo tipo de descuento significa crear una nueva clase, sin tocar el código existente.

challenge icon

Desafío

Fácil

Vamos a construir una calculadora de costos de envío utilizando el Patrón Strategy. Los diferentes métodos de envío tienen diferentes reglas de precios: el envío estándar cobra una tarifa plana, el envío exprés añade un porcentaje y el envío nocturno combina ambos. El Patrón Strategy te permite intercambiar estos cálculos sin cambiar el código que los utiliza.

Organizarás tu código en cuatro archivos:

  • ShippingStrategy.php — Crea una interfaz ShippingStrategy que defina el contrato para todos los métodos de envío. Debe tener un método calculate(float $weight): float que reciba el peso del paquete y devuelva el costo de envío.
  • ShippingMethods.php — Incluye el archivo ShippingStrategy y crea tres clases que implementen la interfaz:
    • StandardShipping — Cobra una tarifa plana de $5.00 independientemente del peso
    • ExpressShipping — Cobra $2.00 por kilogramo (multiplica el peso por 2)
    • OvernightShipping — Cobra una tarifa base de $10.00 más $3.00 por kilogramo
  • ShippingCalculator.php — Incluye el archivo ShippingStrategy y crea una clase ShippingCalculator que actúe como el contexto. Tu calculadora debe:
    • Aceptar una ShippingStrategy en su constructor (usa promoción de constructor)
    • Tener un método setStrategy(ShippingStrategy $strategy): void para cambiar el método de envío en tiempo de ejecución
    • Tener un método calculateCost(float $weight): float que delegue a la estrategia actual
  • main.php — Incluye los archivos ShippingMethods y ShippingCalculator. Recibirás dos entradas: un tipo de envío ("standard", "express", o "overnight") y el peso de un paquete.

    Crea un ShippingCalculator comenzando con StandardShipping. Basándote en la entrada del tipo de envío, usa setStrategy() para cambiar al método de envío apropiado. Luego calcula el costo para el peso dado e imprime el resultado formateado a dos decimales.

    La salida debe ser: Shipping cost: $[cost]

Este desafío demuestra cómo el Patrón Strategy hace que tu sistema de envío sea flexible: agregar un nuevo método de envío como SameDayShipping solo requiere crear una nueva clase que implemente la interfaz, sin tocar la calculadora ni los métodos de envío existentes.

Hoja de referencia

El Patrón Strategy es un patrón de diseño de comportamiento que define una familia de algoritmos, encapsula cada uno en su propia clase y los hace intercambiables en tiempo de ejecución.

Define una interfaz para la estrategia:

<?php
interface DiscountStrategy {
    public function calculate(float $total): float;
}

Implementa estrategias concretas:

<?php
class NoDiscount implements DiscountStrategy {
    public function calculate(float $total): float {
        return $total;
    }
}

class PercentageDiscount implements DiscountStrategy {
    public function __construct(private int $percent) {}
    
    public function calculate(float $total): float {
        return $total - ($total * $this->percent / 100);
    }
}

class FixedDiscount implements DiscountStrategy {
    public function __construct(private float $amount) {}
    
    public function calculate(float $total): float {
        return max(0, $total - $this->amount);
    }
}

Crea una clase de contexto que utilice la estrategia:

<?php
class ShoppingCart {
    public function __construct(private DiscountStrategy $discount) {}
    
    public function setDiscount(DiscountStrategy $discount): void {
        $this->discount = $discount;
    }
    
    public function checkout(float $total): float {
        return $this->discount->calculate($total);
    }
}

Utiliza el patrón inyectando estrategias:

<?php
$cart = new ShoppingCart(new NoDiscount());
echo $cart->checkout(100); // 100

$cart->setDiscount(new PercentageDiscount(20));
echo $cart->checkout(100); // 80

El Patrón Strategy permite cambiar entre algoritmos sin modificar la clase que los utiliza. Añadir nuevas estrategias solo requiere crear nuevas clases que implementen la interfaz.

Pruébalo tú mismo

<?php
require_once 'ShippingMethods.php';
require_once 'ShippingCalculator.php';

// Leer entrada
$shippingType = trim(fgets(STDIN));
$weight = floatval(fgets(STDIN));

// TODO: Crear un ShippingCalculator comenzando con StandardShipping

// TODO: Basado en el tipo de envío ingresado ("standard", "express", o "overnight"),
// usar setStrategy() para cambiar al método de envío apropiado

// TODO: Calcular el costo para el peso dado

// TODO: Imprimir el resultado formateado a dos decimales
// Formato de salida: Shipping cost: $[cost]

?>
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos