Parcial 3: Aplicando patrones de diseño, Enunciado y Resuelto en python by Yuscu Mejia
Parcial 3
Introducción
¡Hola a todos! Soy Yuscu Mejía y en esta lección veremos como resolver un parcial final de la materia programacion en python DE NIVEL UNIVERSIDAD
Ejercicio 2: Refactorizando el código con el Patrón Command
Enunciado:
Como sabemos, el patrón Command enseñado en el curso de MEPACADEMIA
es uno de los más importantes, ya que su propósito es encapsular una solicitud como un objeto, lo que permite parametrizar a los clientes con diferentes solicitudes, encolar solicitudes o registrarlas, y soportar operaciones que se pueden deshacer.
Tu tarea será refactorizar el siguiente código utilizando el patrón Command para mejorar la estructura del sistema. Actualmente, el sistema presenta problemas como duplicación de código, acoplamiento innecesario, y una mala gestión del historial de acciones.
PROBLEMA:
Tienes un sistema simple de un videojuego arcade antiguo donde un jugador puede ejecutar tres acciones: moverse, atacar y defenderse. El sistema está implementado de forma ineficiente, con duplicación de código, acoplamiento innecesario entre clases, y falta de abstracción.
Problemas a resolver:
- Duplicación de lógica: La lógica de ejecución y deshacer se repite para cada acción (mover, atacar, defender), lo que genera redundancia.
- Acoplamiento: Las acciones están acopladas directamente a las clases
PlayeryGame, dificultando la extensión y mantenimiento. - Historia de acciones redundante: Existen dos historiales de acciones, uno en
Gamey otro enPlayer, complicando el seguimiento de las acciones.
Código con malas prácticas:
class Player:
def __init__(self, name):
self.name = name
self.history = []
def move(self, direction):
print(f"{self.name} se mueve {direction}")
self.history.append(("move", direction))
def undo_move(self, direction):
opposites = {'up': 'down', 'down': 'up', 'left': 'right', 'right': 'left'}
opposite_direction = opposites[direction]
print(f"{self.name} se mueve {opposite_direction}")
self.history.append(("move", opposite_direction))
def attack(self):
print(f"{self.name} ataca")
self.history.append(("attack",))
def undo_attack(self):
print(f"{self.name} deshace su ataque.")
self.history.append(("undo_attack",))
def defend(self):
print(f"{self.name} se defiende")
self.history.append(("defend",))
def undo_defend(self):
print(f"{self.name} deja de defender.")
self.history.append(("undo_defend",))
class Game:
def __init__(self):
self.history = []
def move_player(self, player, direction):
player.move(direction)
self.history.append(("move", player, direction))
def attack_player(self, player):
player.attack()
self.history.append(("attack", player))
def defend_player(self, player):
player.defend()
self.history.append(("defend", player))
def undo_last_action(self):
if self.history:
last_action = self.history.pop()
action_type = last_action[0]
if action_type == "move":
player, direction = last_action[1], last_action[2]
player.undo_move(direction)
elif action_type == "attack":
player = last_action[1]
player.undo_attack()
elif action_type == "defend":
player = last_action[1]
player.undo_defend()
else:
print("No hay acciones para deshacer.")
if __name__ == "__main__":
player = Player("Héroe")
game = Game()
# Ejecutando acciones directamente
game.move_player(player, "up")
game.attack_player(player)
game.defend_player(player)
# Deshaciendo acciones
game.undo_last_action() # Deshacer defender
game.undo_last_action() # Deshacer ataque
game.undo_last_action() # Deshacer mover hacia arriba
Refactoriza este código aplicando el patrón de diseño Command, eliminando las malas prácticas mencionadas.
Solución refactorizada:
from abc import ABC, abstractmethod
# Interfaz Command
class Command(ABC):
@abstractmethod
def execute(self):
pass
@abstractmethod
def undo(self):
pass
# Comando para mover al jugador
class MoveCommand(Command):
opposites = {'up': 'down', 'down': 'up', 'left': 'right', 'right': 'left'}
def __init__(self, player, direction):
self.player = player
self.direction = direction
def execute(self):
self.player.move(self.direction)
def undo(self):
opposite_direction = MoveCommand.opposites[self.direction]
self.player.move(opposite_direction)
# Comando para atacar
class AttackCommand(Command):
def __init__(self, player):
self.player = player
def execute(self):
self.player.attack()
def undo(self):
self.player.undo_attack()
# Comando para defender
class DefendCommand(Command):
def __init__(self, player):
self.player = player
def execute(self):
self.player.defend()
def undo(self):
self.player.undo_defend()
# Clase que representa al jugador
class Player:
def __init__(self, name):
self.name = name
def move(self, direction):
print(f"{self.name} se mueve {direction}")
def attack(self):
print(f"{self.name} ataca")
def undo_attack(self):
print(f"{self.name} deshace su ataque.")
def defend(self):
print(f"{self.name} se defiende")
def undo_defend(self):
print(f"{self.name} deja de defender.")
# Clase que representa el juego y ejecuta los comandos
class Game:
def __init__(self):
self.history = []
def execute_command(self, command):
command.execute()
self.history.append(command)
def undo_last_action(self):
if self.history:
last_command = self.history.pop()
last_command.undo()
else:
print("No hay acciones para deshacer.")
if __name__ == "__main__":
player = Player("Héroe")
game = Game()
# Creación de comandos
move_up = MoveCommand(player, "up")
attack = AttackCommand(player)
defend = DefendCommand(player)
# Ejecutando comandos
game.execute_command(move_up)
game.execute_command(attack)
game.execute_command(defend)
# Deshaciendo acciones
game.undo_last_action() # Deshacer defender
game.undo_last_action() # Deshacer ataque
game.undo_last_action() # Deshacer mover hacia arriba
Práctica
Te invito a ver el resuelto de este parcial por youtube
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


