Patrons de conception/Chaîne de responsabilité


Le patron de conception Chaîne de responsabilité 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.

Patron de conception
Catégorie : « Gang of Four »Comportement
Nom français : Chaîne de responsabilité
Nom anglais : Chain-of-responsibility
Permettre à 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.

Utilisation

modifier

Dès lors qu'une information doit recevoir plusieurs traitements, ou juste être transmise entre différents objets.

Variante

modifier

Une variante de ce patron de conception est un arbre de responsabilité, où chaque nœud de traitement transmet l'objet non plus à un seul autre nœud mais à plusieurs nœuds (exemple : un interpréteur de document XML).

Exemples

modifier

L'exemple ci-dessous présente un système de journalisation (log en anglais). Lors de la réception d'un message, ce dernier va passer d'un Logger à l'autre, déclenchant ou non le traitement associé.

#include <iostream>
#include <string>
using namespace std;

class Logger
{
protected:
    int level;
    Logger* next;

public:
    enum
    {
        ERR,
        NOTICE,
        DEBUG
    };

    Logger* setNext(Logger* next)
    {
        this->next = next;
        return (this->next);
    }

    void message(string msg, int priority)
    {
        if (priority <= this->level)
            this->writeMessage(msg);
        if (this->next != NULL)
            this->next->message(msg, priority);
    }

    virtual void writeMessage(string msg) = 0;
};

class DebugLogger : public Logger
{
public:
    DebugLogger(int level)
    {
        this->level = level;
        this->next = NULL;
    }

    void writeMessage(string msg)
    {
        cout << "Message de debug : " << msg << endl;
    }
};

class EmailLogger : public Logger
{
public:
    EmailLogger(int level)
    {
        this->level = level;
        this->next = NULL;
    }

    void writeMessage(string msg)
    {
        cout << "Notification par email : " << msg << endl;
    }
};

class ErrorLogger : public Logger
{
public:
    ErrorLogger(int level)
    {
        this->level = level;
        this->next = NULL;
    }

    void writeMessage(string msg)
    {
        cerr << "Erreur : " << msg << endl;
    }
};

int main()
{
     // Construction de la chaîne de responsabilité
    DebugLogger logger(Logger::DEBUG);
    EmailLogger logger2(Logger::NOTICE);
    ErrorLogger logger3(Logger::ERR);
    logger.setNext(&logger2);
    logger2.setNext(&logger3);

    logger.message("Entering function y.", Logger::DEBUG);    // Utilisé par DebugLogger
    logger.message("Step1 completed.", Logger::NOTICE);    // Utilisé par DebugLogger et EmailLogger
    logger.message("An error has occurred.", Logger::ERR);    // Utilisé par les trois Loggers

    return 0;
}

Le même exemple que le précédent, en Java.

import java.util.*;

/**
    Classe de gestion de journalisation abstraite.
*/
abstract class Logger 
{
    public static final int
        ERR = 0,
        NOTICE = 1,
        DEBUG = 2;

    protected int level;

    /** L'élément suivant dans la chaîne de responsabilité. */
    protected Logger next;

    protected Logger(int level)
    {
        this.level = level;
        this.next = null;
    }

    public Logger setNext( Logger l)
    {
        next = l;
        return l;
    }

    public void message( String msg, int priority )
    {
        if ( priority <= level ) 
            writeMessage( msg );

        if ( next != null )
            next.message( msg, priority );
    }
    
    abstract protected void writeMessage( String msg );
}

/**
    Journalisation sur la sortie standard.
*/
class StdoutLogger extends Logger 
{
    public StdoutLogger( int level ) { super(level); }

    protected void writeMessage( String msg )
    {
        System.out.println( "Writing to stdout: " + msg );
    }
}

/**
    Journalisation par courriel.
*/
class EmailLogger extends Logger 
{
    public EmailLogger( int level ) { super(level); }

    protected void writeMessage( String msg )
    {
        System.out.println( "Sending via email: " + msg );
    }
}

/**
    Journalisation sur l'erreur standard.
*/
class StderrLogger extends Logger 
{
    public StderrLogger( int level ) { super(level); }

    protected void writeMessage( String msg )
    {
        System.err.println( "Sending to stderr: " + msg );
    }
}

/**
    Classe principale de l'application.
*/
public class ChainOfResponsibilityExample
{
    public static void main( String[] args )
    {
        // Construire la chaîne de responsabilité
        //   StdoutLogger -> EmailLogger -> StderrLogger
        Logger l,l1;
        l1 = l = new StdoutLogger( Logger.DEBUG );
        l1 = l1.setNext(new EmailLogger( Logger.NOTICE ));
        l1 = l1.setNext(new StderrLogger( Logger.ERR ));

        // Traité par StdoutLogger
        l.message( "Entering function y.", Logger.DEBUG );

        // Traité par StdoutLogger et EmailLogger
        l.message( "Step1 completed.", Logger.NOTICE );

        // Traité par les trois loggers
        l.message( "An error has occurred.", Logger.ERR );
    }
}

Ce programme affiche :

Writing to stdout:   Entering function y.
Writing to stdout:   Step1 completed.
Sending via e-mail:  Step1 completed.
Writing to stdout:   An error has occurred.
Sending via e-mail:  An error has occurred.
Writing to stderr:   An error has occurred.