Patrón State
Parte de la sección Object Oriented Programming del Journey de Python de Coddy — lección 53 de 64.
El Patrón de Estado permite que un objeto cambie su comportamiento cuando su estado interno cambia. El objeto parece cambiar su clase basándose en su estado actual.
Aquí hay clases de estado simples para un semáforo:
class RedState:
def next_state(self, light):
print("Red -> Green")
light.state = GreenState()
def current_color(self):
return "Red"
class GreenState:
def next_state(self, light):
print("Green -> Yellow")
light.state = YellowState()
def current_color(self):
return "Green"
class YellowState:
def next_state(self, light):
print("Yellow -> Red")
light.state = RedState()
def current_color(self):
return "Yellow"Cada estado define lo que sucede al transicionar al siguiente estado.
Crea una clase de contexto que mantenga el estado actual:
class TrafficLight:
def __init__(self):
self.state = RedState() # Empezar con rojo
def change(self):
self.state.next_state(self)
def get_color(self):
return self.state.current_color()El semáforo delega el comportamiento a su objeto de estado actual.
Usa el semáforo:
light = TrafficLight()
print(f"Current: {light.get_color()}")
light.change() # Rojo -> Verde
print(f"Current: {light.get_color()}")
light.change() # Verde -> Amarillo
print(f"Current: {light.get_color()}")
light.change() # Amarillo -> Rojo
print(f"Current: {light.get_color()}")Crea otro ejemplo con un reproductor simple:
class PlayingState:
def play(self, player):
print("Already playing")
def stop(self, player):
print("Stopping music")
player.state = StoppedState()
class StoppedState:
def play(self, player):
print("Starting music")
player.state = PlayingState()
def stop(self, player):
print("Already stopped")
class MusicPlayer:
def __init__(self):
self.state = StoppedState()
def play(self):
self.state.play(self)
def stop(self):
self.state.stop(self)
player = MusicPlayer()
player.play() # Iniciando música
player.play() # Ya se está reproduciendo
player.stop() # Deteniendo música
player.stop() # Ya está detenidoSalida:
Current: Red
Red -> Green
Current: Green
Green -> Yellow
Current: Yellow
Yellow -> Red
Current: Red
Starting music
Already playing
Stopping music
Already stoppedPunto clave: El patrón State encapsula el comportamiento específico del estado en clases separadas y permite que el objeto de contexto delegue al estado actual. Cuando el estado cambia, el comportamiento cambia automáticamente. Esto elimina sentencias if/else complejas y facilita la adición de nuevos estados.
Desafío
FácilEn este desafío, implementarás la clase State en state.py con el encapsulamiento y los métodos adecuados. El archivo contiene comentarios TODO detallados para guiar tu implementación paso a paso.
El conjunto de pruebas está diseñado para verificar que tu implementación cumpla con todos los requisitos y maneje las condiciones excepcionales correctamente. Utiliza los resultados de las pruebas para comprender el comportamiento esperado y refinar tu solución.
Hoja de referencia
El Patrón State permite que un objeto cambie su comportamiento cuando su estado interno cambia. El objeto parece cambiar su clase basándose en su estado actual.
Crea clases de estado que definan el comportamiento para cada estado:
class RedState:
def next_state(self, light):
print("Red -> Green")
light.state = GreenState()
def current_color(self):
return "Red"
class GreenState:
def next_state(self, light):
print("Green -> Yellow")
light.state = YellowState()
def current_color(self):
return "Green"Crea una clase de contexto que mantenga el estado actual:
class TrafficLight:
def __init__(self):
self.state = RedState() # Comenzar con rojo
def change(self):
self.state.next_state(self)
def get_color(self):
return self.state.current_color()Ejemplo de uso:
light = TrafficLight()
print(f"Current: {light.get_color()}") # Actual: Red
light.change() # Red -> Green
print(f"Current: {light.get_color()}") # Actual: GreenEl Patrón State encapsula el comportamiento específico del estado en clases separadas y permite que el objeto de contexto delegue al estado actual. Esto elimina sentencias if/else complejas y facilita la adición de nuevos estados.
Pruébalo tú mismo
from state import State
# Manejador integral de casos de prueba
test_case = input()
if test_case == "basic_test":
obj = State("Test Name")
obj.display_info()
elif test_case == "validation_test":
obj = State("Validation Test")
print(f"Name: {obj.name}")
elif test_case == "empty_name_test":
obj = State("")
obj.display_info()
print(f"Name property: {obj.name}")
elif test_case == "special_chars_test":
obj = State("Test@#$%")
obj.display_info()
print(f"Name property: {obj.name}")
elif test_case == "long_name_test":
long_name = "A" * 100
obj = State(long_name)
obj.display_info()
print(f"Name length: {len(obj.name)}")
elif test_case == "multiple_states_test":
states = [
State("California"),
State("Texas"),
State("New York"),
State("Florida")
]
for state in states:
state.display_info()
print(f"Name via property: {state.name}")
elif test_case == "attribute_access_test":
obj = State("Access Test")
# Acceso directo al atributo privado (no recomendado)
print(f"Direct access (not recommended): {obj._name}")
# Acceso a través de la propiedad (recomendado)
print(f"Property access (recommended): {obj.name}")
elif test_case == "property_behavior_test":
obj = State("Original Name")
print(f"Original name: {obj.name}")
# Intentar modificar el nombre (fallará ya que no hay un setter)
try:
obj.name = "New Name"
except AttributeError as e:
print(f"Error when trying to modify name: {e}")
# Mostrar que el nombre no ha cambiado
print(f"Name after modification attempt: {obj.name}")Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Object Oriented Programming
1Fundamentos de la POO
Archivos externosIntroducción a la POOClases vs ObjetosEl parámetro selfMétodosAtributosMétodo constructor (__init__)Resumen - Calculadora simple4Herencia
Herencia básicaLa función super()Sobrescritura de métodosHerencia múltipleOrden de resolución de métodosResumen - Jerarquía de empleados7Métodos especiales
Introducción a los métodos mágicosSobrecarga de operadoresMétodos mágicos de contenedoresRepaso - Lista personalizada10Patrones de diseño Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy2Decoradores
Introducción a los decoradoresDecorador propertyDecorador de métodos estáticosDecorador de métodos de clase5Polimorfismo
Sobreescritura de métodos revisitadaDuck TypingClases y métodos abstractosDiseño de interfacesResumen - Calculadora de figuras8Conceptos avanzados de POO
Composición vs. HerenciaMixinsMétodos estáticos y de claseDecoradores de claseGestores de contexto11Patrones de diseño, parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón Composite3Propiedades de clase
Variables de instancia vs de claseDecoradores de propiedadesAtributos privadosResumen - Gestor de cuentas bancarias