Le motif state
Définition
Le motif etat permet de mettre en place un objet dont le comportement changeras en fonction de sont état.
Principe
Pour réaliser ce motif, il faut réaliser des sous objets qui se comporterons comme l’objet principal, mais dans un état particulier. Ce n’est pas un mais plusieurs objet que nous devons réaliser.
L’exemple par le code
Nous allons mettre en place notre système de gestion des prises USB.
Bien, commençons par écrire notre objet device_manager
class Device_manager
attr_accessor :state
attr_reader :port, :no_device, :available_device, :mounted_device, :in_transfert
def initialize(port = "/dev/null")
@no_device = No_device.new(self)
@available_device = Available_device.new(self)
@mounted_device = Mounted_device.new(self)
@in_transfert = In_transfert_device.new(self)
@port = port
@state = @no_device
end
def copy
@state.copy
end
def mount
@state.mount
end
def umount
@state.umount
end
def check
@state.check
end
end
Nous avons besoin pour ce manager de connaitre le “port” d’écoute (c’est en fait un fichier système, mais ce n’est pas le sujet). Ensuite on défini les divers methodes dont nous allons avoir besoin pour notre exemple. Un attribut @state seras le receptacle de l’état courant de notre manager. C’est en fait la représentation informatique de l’état de notre port USB.
Nous allons maintenant ecrire les objets No_device, Available_device, Mounted_device et In_transfert_device qui comme vous l’aurais deviné represente les états possible de notre manager. C’est là toute la clé de ce motif: faire un objet qui encapsule le comportement du manager en fonction de sont état.
Pour réalisé ces objets nosu allons faire un module qui permettras de définir tout les comportement “par défaut”.
module State
attr_reader :device_manager
def initialize(device_manager)
@device_manager = device_manager
end
def copy
puts "Pas de clé montée"
end
def mount
puts "Pas de clé branché"
end
def umount
puts "Il n'y a rien à démonter"
end
def check
puts "Pas de clé branché"
end
end
class No_device
include State
def check
puts "regarde si un device est branché. Oui"
@device_manager.state = @device_manager.available_device
end
end
class Available_device
include State
def mount
puts "Montage du device"
@device_manager.state = @device_manager.mounted_device
end
end
class Mounted_device
include State
def mount
puts "Le device est occupé"
end
def umount
puts "démontage du device"
@device_manager.state = @device_manager.available_device
end
def copy
puts "Lancement de la copie"
@device_manager.state = @device_manager.in_transfert
end
end
class In_transfert_device
include State
def umount
@device_manager.state = @device_manager.mounted_device
@device_manager.umount
end
def copy
puts "Transerf en cours"
end
def mount
puts "Clé déjà montée"
end
def check
puts "Clé branché"
end
end
On a redéfini ce qui était à redéfinir dans chacun des objets en fonction de l’état qu’il représente. Ce sont ces objets qui modifie l’état, ce sont les mieux placé pour le faire.
Voyons ce que ça donne:
device = Device_manager.new("/dev/usb0")
device.check
device.mount
device.mount
device.copy
device.mount
device.umount
device.umount
regarde si un device est branché. Oui Montage du device Le device est occupé Lancement de la copie Clé déjà montée démontage du device Il n'y a rien à démonter
Pour bien faire il faudrais que le check soit déclenché par un système d’observateur de démon. Ce dernier observerais le port en permanence. De même que notre simulation de copie ne devrais accepter le changement d’état qu’une fois la copie fini (ici c’est instantané, pour les besoin de l’exemple). Bref c’est un exemple d’utilisation du motif état.
Vous avez surement remarqué les similitudes avec le motif strategie. Voici la différence:
motif état:
Ensemble de comportement encapsulés dans des objets état.
C’est la machine à état qui sais quel objet appeler (dans notre cas la machine à état c’est le device_manager.
motif strategie: C’est le client qui indique l’objet strategie à utiliser dans le contexte.


