Lección 8.1 : Patron de diseño Strategy
Patrón de Diseño Strategy en Python
Introducción
¡Hola a todos! Soy Yuscu Mejía y en esta lección vamos a explorar el concepto del patrón de diseño Strategy en Python. El patrón Strategy permite definir una familia de algoritmos, encapsular cada uno de ellos y hacerlos intercambiables. Esto permite que el algoritmo varíe independientemente de los clientes que lo utilizan.
Características
El patrón Strategy en Python tiene las siguientes características:
- Permite definir una familia de algoritmos.
- Encapsula cada algoritmo y los hace intercambiables.
- Permite variar el algoritmo independientemente de los clientes que lo utilizan.
- Facilita el mantenimiento y extensión de código al seguir el principio abierto/cerrado.
Propósito
El patrón de diseño Strategy permite definir una familia de algoritmos, encapsular cada uno de ellos y hacerlos intercambiables. Strategy permite que el algoritmo varíe independientemente de los clientes que lo utilizan.
Problema
El problema que resuelve el patrón Strategy es el de permitir seleccionar un algoritmo en tiempo de ejecución. Esto es útil cuando se tienen diferentes maneras de realizar una tarea y se desea que el cliente pueda escoger cuál utilizar.
Solución
La solución propuesta por el patrón Strategy es crear una jerarquía de estrategias, cada una implementando un algoritmo específico. El contexto utiliza una referencia a la interfaz Strategy para llamar al algoritmo definido por una estrategia concreta.
Analogía en el Mundo Real
Una analogía del mundo real sería un navegador GPS que puede ofrecer diferentes rutas: la más rápida, la más corta, o la más escénica. El usuario puede seleccionar la estrategia de navegación que mejor se adapte a sus necesidades en ese momento.
Estructura
La estructura del patrón Strategy incluye los siguientes componentes:
- Strategy: Interfaz común para todas las estrategias.
- ConcreteStrategy: Implementaciones concretas de la interfaz Strategy.
- Context: Mantiene una referencia a una Strategy y la utiliza para ejecutar el algoritmo.

- Flecha de Línea Llena con Cabeza de Triángulo: Conecta la interfaz Strategy con ConcreteStrategies,
significado es que esta flecha indica herencia o implementación, las ConcreteStrategies implementan la interfaz Strategy.
Es decir, las clases concretas (ConcreteStrategies) son subclases que heredan de la interfaz Strategy y deben proporcionar una implementación para el método execute(data). - Flecha Sin Puntear de Client a Context :la relación es más permanente, y Client probablemente tengauna referencia directa a un objeto de tipo Context durante su vida útil.
- Flecha Punteada de Client a ConcreteStrategies: Indica que Client tiene una dependencia débil o temporal sobre las estrategias concretas
- Flecha de Línea Llena con Rombo Blanco (Composición débil o Agregación):Significado: Indica que Context tiene una relación de agregación con Strategy. Esto significa que Context tiene una referencia a un objeto de tipo Strategy, pero Strategy puede existir independientemente de Context
Aplicabilidad
El patrón Strategy es aplicable cuando:
- Se tienen múltiples algoritmos para una tarea específica y se desea seleccionar entre ellos en tiempo de ejecución.
- Se desea evitar múltiples condicionales que seleccionan qué algoritmo utilizar.
- Se desea poder cambiar o añadir nuevos algoritmos sin modificar el contexto.
Pros y Contras
- Pros:
- Permite cambiar algoritmos en tiempo de ejecución.
- Promueve la reutilización del código al separar los algoritmos en clases distintas.
- Facilita añadir nuevas estrategias sin modificar el contexto.
- Contras:
- Introduce complejidad adicional debido a la creación de múltiples clases.
- El cliente debe ser consciente de las diferentes estrategias y cómo utilizarlas.
Vamos a ver uso Patron Strategy vs Programación Estructural con condicional
A continuación, un ejemplo de cómo utilizar el patrón Strategy en Python, presentados por Yuscu Mejía:
-
- Patron Strategy:
# Definir la interfaz Strategy
class Strategy:
def ejecutar(self, a, b):
pass
# Definir estrategias concretas
class StrategySuma(Strategy):
def ejecutar(self, a, b):
return a + b
class StrategyMultiplicacion(Strategy):
def ejecutar(self, a, b):
return a * b
# Definir el contexto que utiliza una Strategy
class ContextoCalculadora:
def __init__(self, strategy: Strategy):
self._strategy = strategy
def set_strategy(self, strategy: Strategy):
self._strategy = strategy
def ejecutar_estrategia(self, a, b):
return self._strategy.ejecutar(a, b)
# Ejemplo de uso
contexto = ContextoCalculadora(StrategySuma())
print(contexto.ejecutar_estrategia(3, 4)) # Imprime: 7
contexto.set_strategy(StrategyMultiplicacion())
print(contexto.ejecutar_estrategia(3, 4)) # Imprime: 12
-
- Programación Estructural con Condicionales
# Definir las estrategias como funciones
def estrategia_suma(a, b):
return a + b
def estrategia_multiplicacion(a, b):
return a * b
# Definir el contexto que utiliza una estrategia
def contexto_calculadora(tipo_estrategia, a, b):
if tipo_estrategia == "suma":
return estrategia_suma(a, b)
elif tipo_estrategia == "multiplicacion":
return estrategia_multiplicacion(a, b)
else:
raise ValueError("Estrategia desconocida")
# Ejemplo de uso
tipo_estrategia = "suma"
resultado = contexto_calculadora(tipo_estrategia, 3, 4)
print(resultado) # Imprime: 7
tipo_estrategia = "multiplicacion"
resultado = contexto_calculadora(tipo_estrategia, 3, 4)
print(resultado) # Imprime: 12
Comparación de Ventajas
Patrón Strategy
- Flexibilidad: Es fácil añadir nuevas estrategias sin modificar el contexto. Solo se necesita crear una nueva clase que implemente la interfaz Strategy.
- Mantenimiento: El código es más modular y cada estrategia está encapsulada en su propia clase, lo que facilita la lectura y el mantenimiento del código.
- Extensibilidad: Puedes cambiar dinámicamente las estrategias en tiempo de ejecución sin necesidad de modificar el contexto ni añadir nuevas condiciones.
Programación Estructural con Condicionales
- Simplicidad Inicial: Para casos sencillos con pocas estrategias, puede ser más directo y fácil de entender al principio.
- Menos Código Inicial: No requiere definir clases adicionales, lo cual puede ser útil para scripts simples o pequeños programas.
Práctica
Para practicar, les invito a ver nuestro tutorial en video sobre el patrón de diseño Strategy en Python, presentado por Yuscu Mejía:
Recorda si te gusta los videos, te agradeceria si le pones un comentario o un like para saber si subir mas videos! 😮
Si no sabes que comentar, comenta con: estoy avanzando rapido con el curso de Programacion python 🙂 asi te detectaremos que viniste de aca, para una estadistica si conviene o no subir videos

Ejercicio
Ahora les propongo un ejercicio para aplicar lo que hemos aprendido sobre el patrón de diseño Strategy en Python, con la guía de Yuscu Mejía:
Ejercicio: Implementación del Patrón Strategy
En este ejercicio, vamos a crear una clase llamada ContextoCifrado que pueda utilizar diferentes estrategias para cifrar y descifrar mensajes. Implementaremos dos estrategias: una para el cifrado César y otra para el cifrado inverso.
Sigue estos pasos para completar el ejercicio:
- Define una interfaz
EstrategiaCifradocon un métodocifrarque toma un mensaje como argumento. - Crea dos clases que implementen la interfaz
EstrategiaCifrado:CifradoCesaryCifradoInverso. - La clase
CifradoCesardebe desplazar cada letra del mensaje un número fijo de posiciones en el alfabeto. - La clase
CifradoInversodebe invertir el orden de los caracteres en el mensaje. - Define una clase
ContextoCifradoque utilice una estrategia de cifrado. - Permite que la estrategia de cifrado pueda ser cambiada en tiempo de ejecución.
Veamos cómo Yuscu Mejía implementaría la solución a este ejercicio:
#Ejercicio Resuelto
# Definir la interfaz EstrategiaCifrado
class EstrategiaCifrado:
def cifrar(self, mensaje):
pass
# Definir la estrategia de cifrado César
class CifradoCesar(EstrategiaCifrado):
def cifrar(self, mensaje):
desplazamiento = 3
cifrado = ''
for char in mensaje:
if char.isalpha():
desplazado = ord(char) + desplazamiento
if char.islower():
if desplazado > ord('z'):
desplazado -= 26
cifrado += chr(desplazado)
elif char.isupper():
if desplazado > ord('Z'):
desplazado -= 26
cifrado += chr(desplazado)
else:
cifrado += char
return cifrado
# Definir la estrategia de cifrado inverso
class CifradoInverso(EstrategiaCifrado):
def cifrar(self, mensaje):
return mensaje[::-1]
# Definir el contexto que utiliza una EstrategiaCifrado
class ContextoCifrado:
def __init__(self, estrategia: EstrategiaCifrado):
self._estrategia = estrategia
def set_estrategia(self, estrategia: EstrategiaCifrado):
self._estrategia = estrategia
def ejecutar_estrategia(self, mensaje):
return self._estrategia.cifrar(mensaje)
# Ejemplo de uso
contexto = ContextoCifrado(CifradoCesar())
print(contexto.ejecutar_estrategia("Hola Mundo")) # Imprime: Krod Pxqgr
contexto.set_estrategia(CifradoInverso())
print(contexto.ejecutar_estrategia("Hola Mundo")) # Imprime: odnuM aloH

