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.