Menu
Coddy logo textTech

Patron État

Fait partie de la section Object Oriented Programming du Journey Python de Coddy — leçon 53 sur 64.

Le patron d'état (State Pattern) permet à un objet de modifier son comportement lorsque son état interne change. L'objet semble changer de classe en fonction de son état actuel.

Voici des classes d'état simples pour un feu de signalisation :

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"

Chaque état définit ce qui se passe lors de la transition vers l'état suivant.

Créez une classe de contexte qui contient l'état actuel :

class TrafficLight:
    def __init__(self):
        self.state = RedState()  # Commencer par le rouge
    
    def change(self):
        self.state.next_state(self)
    
    def get_color(self):
        return self.state.current_color()

Le feu de signalisation délègue le comportement à son objet d'état actuel.

Utilisez le feu de signalisation :

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

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

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

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

Créez un autre exemple avec un lecteur 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()   # Démarrage de la musique
player.play()   # Déjà en cours de lecture
player.stop()   # Arrêt de la musique
player.stop()   # Déjà arrêté

Sortie :

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

Point clé : Le State Pattern encapsule le comportement spécifique à l'état dans des classes séparées et laisse l'objet contexte déléguer à l'état actuel. Lorsque l'état change, le comportement change automatiquement. Cela élimine les instructions if/else complexes et facilite l'ajout de nouveaux états.

challenge icon

Défi

Facile

Dans ce défi, vous implémenterez la classe State dans state.py avec une encapsulation et des méthodes appropriées. Le fichier contient des commentaires TODO détaillés pour guider votre implémentation étape par étape.

La suite de tests est conçue pour vérifier que votre implémentation répond à toutes les exigences et gère correctement les conditions exceptionnelles. Utilisez les résultats des tests pour comprendre le comportement attendu et affiner votre solution.

Aide-mémoire

Le patron d'état (State Pattern) permet à un objet de modifier son comportement lorsque son état interne change. L'objet semble changer de classe en fonction de son état actuel.

Créez des classes d'état qui définissent le comportement pour chaque état :

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"

Créez une classe de contexte qui détient l'état actuel :

class TrafficLight:
    def __init__(self):
        self.state = RedState()  # Commencer par le rouge
    
    def change(self):
        self.state.next_state(self)
    
    def get_color(self):
        return self.state.current_color()

Exemple d'utilisation :

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

Le patron d'état encapsule le comportement spécifique à l'état dans des classes séparées et laisse l'objet de contexte déléguer à l'état actuel. Cela élimine les instructions if/else complexes et facilite l'ajout de nouveaux états.

Essayez vous-même

from state import State

# Gestionnaire de cas de test complet
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")
    # Accès direct à l'attribut privé (non recommandé)
    print(f"Direct access (not recommended): {obj._name}")
    # Accès via la propriété (recommandé)
    print(f"Property access (recommended): {obj.name}")

elif test_case == "property_behavior_test":
    obj = State("Original Name")
    print(f"Original name: {obj.name}")
    
    # Tenter de modifier le nom (échouera car il n'y a pas de setter)
    try:
        obj.name = "New Name"
    except AttributeError as e:
        print(f"Error when trying to modify name: {e}")
    
    # Montrer que le nom est inchangé
    print(f"Name after modification attempt: {obj.name}")
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Object Oriented Programming