Menu
Coddy logo textTech

Padrão State

Parte da seção Object Oriented Programming do Journey de Python da Coddy — lição 53 de 64.

O Padrão State permite que um objeto altere seu comportamento quando seu estado interno muda. O objeto parece mudar sua classe com base em seu estado atual.

Aqui estão classes de estado simples para um 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 o que acontece ao transitar para o próximo estado.

Crie uma classe de contexto que mantém o estado atual:

class TrafficLight:
    def __init__(self):
        self.state = RedState()  # Comece com vermelho
    
    def change(self):
        self.state.next_state(self)
    
    def get_color(self):
        return self.state.current_color()

O semáforo delega o comportamento ao seu objeto de estado atual.

Use o semáforo:

light = TrafficLight()
print(f"Current: {light.get_color()}")

light.change()  # Vermelho -> Verde
print(f"Current: {light.get_color()}")

light.change()  # Verde -> Amarelo
print(f"Current: {light.get_color()}")

light.change()  # Amarelo -> Vermelho
print(f"Current: {light.get_color()}")

Crie outro exemplo com um player simples:

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()   # Já está tocando
player.stop()   # Parando a música
player.stop()   # Já está parado

Saída:

Current: Red
Red -> Green
Current: Green
Green -> Yellow
Current: Yellow
Yellow -> Red
Current: Red
Starting music
Already playing
Stopping music
Already stopped

Ponto Chave: O Padrão State encapsula o comportamento específico do estado em classes separadas e permite que o objeto de contexto delegue para o estado atual. Quando o estado muda, o comportamento muda automaticamente. Isso elimina declarações if/else complexas e torna a adição de novos estados mais fácil.

challenge icon

Desafio

Fácil

Neste desafio, você implementará a classe State em state.py com encapsulamento e métodos adequados. O arquivo contém comentários TODO detalhados para orientar sua implementação passo a passo.

A suíte de testes foi projetada para verificar se sua implementação atende a todos os requisitos e lida corretamente com condições excepcionais. Use os resultados dos testes para entender o comportamento esperado e refinar sua solução.

Folha de consulta

O Padrão State permite que um objeto altere seu comportamento quando seu estado interno muda. O objeto parece mudar sua classe com base em seu estado atual.

Crie classes de estado que definam o comportamento 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"

Crie uma classe de contexto que mantenha o estado atual:

class TrafficLight:
    def __init__(self):
        self.state = RedState()  # Começa com vermelho
    
    def change(self):
        self.state.next_state(self)
    
    def get_color(self):
        return self.state.current_color()

Exemplo de uso:

light = TrafficLight()
print(f"Current: {light.get_color()}")  # Atual: Red
light.change()  # Red -> Green
print(f"Current: {light.get_color()}")  # Atual: Green

O Padrão State encapsula o comportamento específico do estado em classes separadas e permite que o objeto de contexto delegue para o estado atual. Isso elimina declarações if/else complexas e facilita a adição de novos estados.

Experimente você mesmo

from state import State

# Manipulador abrangente de casos de teste
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")
    # Acesso direto ao atributo privado (não recomendado)
    print(f"Direct access (not recommended): {obj._name}")
    # Acesso via propriedade (recomendado)
    print(f"Property access (recommended): {obj.name}")

elif test_case == "property_behavior_test":
    obj = State("Original Name")
    print(f"Original name: {obj.name}")
    
    # Tenta modificar o nome (falhará pois não há um setter)
    try:
        obj.name = "New Name"
    except AttributeError as e:
        print(f"Error when trying to modify name: {e}")
    
    # Mostra que o nome permanece inalterado
    print(f"Name after modification attempt: {obj.name}")
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Object Oriented Programming