Chaîne de responsabilité

Chaîne de responsabilité (Chain of reponsability) en génie logiciel est un patron de conception qui permet à un nombre quelconque de classes d’essayer de répondre à une requête sans connaître les possibilités des autres classes sur cette requête.

Cela permet de diminuer le couplage entre objets. Le seul lien commun entre ces objets étant cette requête qui passe d’un objet à l’autre jusqu’à ce que l’un des objets puisse répondre. Ce patron de conception permet aussi de séparer les différentes étapes d’un traitement et d’implémenter facilement les relations d’héritage.

Quand l’utiliser ?

Le patron Chaîne de responsabilité est à utiliser dès lors qu’une information doit recevoir plusieurs traitements, ou alors être transmise entre différents objets.

Exemple

Pour illustrer ce patron de conception nous allons prendre l’exemple d’un distributeur de billets.

Premièrement nous allons créer une interface IMonnaie qui permettra d’aller sur la monnaie suivante, de propager le montant restant à restituer et une méthode pour obtenir le montant.

Une classe TMonnaie supportant l’interface IMonnaie est ajoutée.

type
  IMonnaie = interface
    procedure Suivant(aMonnaie: IMonnaie);
    procedure RendreMonnaie(aMontantRestant: integer);
    function GetMontant: integer;
  end;
 
  TMonnaie = class(TInterfacedObject, IMonnaie)
  strict private
    FSuivant: IMonnaie;
    FMontant: integer;
  public
    constructor Create(aMontant: integer);
 
    procedure Suivant(aMonnaie: IMonnaie);
    procedure RendreMonnaie(aMontantRestant: integer);
    function GetMontant: integer;
  end;
 
{ TBillet }
 
constructor TMonnaie.Create(aMontant: integer);
begin
  FMontant := aMontant;
  FSuivant := nil;
end;
 
function TMonnaie.GetMontant: integer;
begin
  Result := FMontant;
end;
 
procedure TMonnaie.Suivant(aMonnaie: IMonnaie);
begin
  FSuivant := aMonnaie;
end;

Ensuite pour rendre la monnaie, si le montant à restituer est inférieur au montant de la monnaie, il faut aller à la monnaie suivante.

procedure TMonnaie.RendreMonnaie(aMontantRestant: integer);
var
  Qte: integer;
begin
  Qte := 0;
 
  // on boucle tant qu'on peux rendre la monnaie
  while aMontantRestant >= GetMontant do
  begin
    Inc(Qte);
    Dec(aMontantRestant, GetMontant);
  end;
 
  if Qte > 0 then
    Writeln(Qte.ToString + ' x ' + GetMontant.ToString);
 
  // si il reste quelque chose on passe aussi au billet suivant
  if aMontantRestant > 0 then
  begin
    if Assigned(FSuivant) then
      FSuivant.RendreMonnaie(aMontantRestant)
    else
      raise Exception.Create(Format('Erreur il reste %s euros à rendre.', [aMontantRestant]));
  end;
end;

Ensuite nous allons créer le distributeur. Lors de la création le montant à retirer est spécifié.

TDistributeur = class
strict private
  First: IMonnaie;
public
  constructor Create;
  procedure RetirerMonnaie(aMontant: integer);
end;

Le distributeur est chargé de créer des monnaies avec des montant différents.

{ TDistributeur }
 
constructor TDistributeur.Create;
var
  Billet20 : IMonnaie;
  Billet10 : IMonnaie;
  Billet5  : IMonnaie;
begin
  inherited Create;
 
  First    := TMonnaie.Create(50);
  Billet20 := TMonnaie.Create(20);
  Billet10 := TMonnaie.Create(10);
  Billet5  := TMonnaie.Create(5);
 
  First.Suivant(Billet20);
  Billet20.Suivant(Billet10);
  Billet10.Suivant(Billet5);
end;
 
procedure TDistributeur.RetirerMonnaie(aMontant: integer);
begin
  Writeln('Retirer ', aMontant, ' euros');
  First.RendreMonnaie(aMontant);
end;

Enfin, pour retirer 460 euros il suffit d’appeler la méthode du distributeur. Celui-ci va boucler et afficher le nombre de billets correspondant au montant demandé par l’utilisateur.

procedure Tester;
var
  Distributeur: TDistributeur;
begin
  Distributeur := TDistributeur.Create;
  try
    Distributeur.RetirerMonnaie(460);
  finally
    Distributeur.Free;
  end;
 
  Readln;
end;

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

*