The Chain of Responsibility Pattern

天大地大妈咪最大 提交于 2019-12-12 13:56:36

问题


Could somebody provide a simple explanation of the chain of responsibility pattern? I found the wiki article a bit confusing.


回答1:


A very good example are java servlet filters - pieces of code that are executed before the HTTP request arrives at its target.

  • the chain contains multiple instances, and each of them performs a different action
  • each instance in the chain can choose to propagate to the next instance, or stop the flow

So, with servlet filters, you can have

  • a filter that checks if the user is authenticated. If he is, the filter propagates to the next filter

  • the next filter checks if the user has permissions to the current resource. If it does, it propagate to the next

  • the next logs the current request URL and the username, and always propagate to the next

  • there is nothing else in the chain, so the target object is finally invoked




回答2:


I'll try with the help of an analogy:

Think of the command being handled as a hockey puck and the chain of responsibility handler classes as nets with single holes. Now imagine such nets of a varying radius are stacked on top of each other (net with smallest radius hole on top).

Now you drop the puck from top. If the radius of the puck is larger than the first hole, it will get stuck in it and not fall any lower. Meaning the command has been handled by the first handler.

But if the puck is small than the hole it will go through it to the next one and so on until it gets caught or falls through all the nets. All the nets (responsibility handler classes) that the puck goes through have handled the puck (handled the command).




回答3:


With this pattern, you create a chain of objects that examine a request. Each in turn examines the request and either handles it or passes it to the next object in the chain.

Benefits

  • decouples the sender of the request and its receivers
  • simplifies the object because it doesn have to know the chain structure and keep references to its members
  • allows the dynamic adding or removal of responsibility by changing the order or members of the chain

Drawbacks

  • execution of the request isn't garanteed, it may fall off the chain if no object handles it
  • runtime characteristics can be hard to observe and debug

Potential Use cases

  • mouse clicks and keyboard events.
  • email. For example, email is received and passed to the first handler, the spam handler. It is then either processed or passed to the second handler, etc.

From:


Here is an interesting InformIT article on this pattern, with sample code.




回答4:


The post is protected but I want to give an answer because I think that it may be improved.

Chain of responsibility and Filters. Are they the same thing ?

Filter pattern is near from chain of responsibility pattern.
But it is far enough to not mix them.
Using the same term to express two distinct concepts would be a pity.

In filter/interceptor pattern, we have not the notion of responsibility since multiple nodes of the chain may act on the same flow and in the intent of the pattern, it was created for multiple processings. The nodes of chain don't handle the request, they co-participate to the handling of the request.
So, a filter or an interceptor is more a chain of processing than a chain of responsibility.
AOP for example works as filters and interceptors. We want to repeat some processing from a stack of processors.
It is not a question of responsibility but a question of multiple layer of processing that we apply according to some conditions or not. It has important consequences on the implementation and the logic behind it.
Filters/interceptors stored in a chain may have (and have often) no logic or functional relation between them while nodes of a chain of responsibility have always a logic or functional relation between them since they have to handle the same concern. For example, in a chain filter, the first filter may handle logging concern, the second filter, security concern and the last, encoding concern...
In a chain of responsibility, the same concern is handled by all nodes of the chain.

GOF reference Chain of responsibility Intent :

Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it. In chain of responsibility pattern, the chain is stopped when an node of the chain handle that.


Now, I will describe briefly what is it and when using it with a simple and concrete example.

What is the chain of responsibility?

The chain of responsibility design pattern is a behavioral pattern. As all GOF design patterns, its name suits it perfectly since the pattern defines for a request to handle, a chain of objets which, turn-to-turn have the ability to stop the chain processing and to response to the request. This pattern offers as advantage to decouple components of the chain. So, we could update components independently and create dynamically the chain.

When using the chain of responsibility?

In a chain of responsibility, only a single object can take the responsibility to respond to the request. If we wish that more than one candidate to be able to act on the request, we stay away from the chain of responsibility pattern. We are in a processing chain. The filter pattern addresses this kind of need.

The chain of responsibility pattern is applied in multiple contexts : technical as business.

Handling internal and user events in user interfaces is a often described usage. In this context, the chain allows graphical layers and components to handle user inputs such as mouse clicks, pressed keys, etc… but also internal processing such as graphical update processings.

An example with chain of responsibility

We will take the example of a click on a generic button to illustrate that.
The button is "generic" because its behavior depends on the context.

The idea is that graphical components are sorted in a chain from the more local component related to the user action to the less local component related to the user action.
As soon as a component of the chain decides to handle the request, the flow of the chain is stopped.

Here a example to illustrate that.
Imagine a "save" button in the menu bar of a spreadsheet application.
Saving a document which contains 2 spreadsheets should not be done by the first or the second spreadsheet since they should not be directly coupled between them.
Suppose, when the "save" button is pressed, the view is displayed on the second spreadsheet. So, the chain is executed by starting by the second spreadsheet component since it is the more local component related to the action. The second spreadsheet component doesn't consider responsible to handle the request, so they let a more up level component of the chain handle it, for example : the application component. When it receives the request, the application component has all needed information to handle the saving request, so it performs it.
On the contrary, in cases where we have only one spreadsheet in the document, we could imagine that the action may be performed by the spreadsheet component which is self-sufficient.




回答5:


The best way to understand is to analyze a simple example. That's him:

The program translates a word from English to other languages (very simplified version :)) Translated word is passed on to subsequent dictionaries; dictionaries form a chain.

using System;

// The 'Handler' abstract class
abstract class Handler
{
    //chain link
    private Handler _successor;
    //
    static private Handler _first;

    public Handler Successor
    {
        set 
        {
            _successor = value;
        }
        get 
        {
            return _successor;
        }
    }
    public Handler First
    {
        set 
        {
            _first = value;
        }
        get 
        {
           return  _first;
        }
    }
    //
    public void HandleRequest(string request)
    {
        if (First == this)
        {
            Console.WriteLine("\n\tWe translate word => \"{0}\"\n", request);
            First.Translator(request);
        }
        //
        if (Successor != null)
        {
            //Translation by the successor's dictionary 
            Successor.Translator(request);

            //Transfer of word (request) to another chain (dictionary) 
            Successor.HandleRequest(request);
        }
    }
    //
    abstract public void Translator(string word);
}

//The concrete class
class GermanDictionary : Handler
{
    override public void Translator(string word)
    {
        switch (word)
        {
            case "Job":
                word = "Arbeit";
                break;
            case "Rest":
                word = "Rest";
                break;
        }
        Console.WriteLine("\t\tinto German => \"{0}\"", word);
    }
}

class FrenchDictionary : Handler
{
    override public void Translator(string word)
    {
        switch (word)
        {
            case "Job":
                word = "Travail";
                break;
            case "Rest":
                word = "Reste";
                break;
        }
        Console.WriteLine("\t\tinto French => \"{0}\"", word);
    }
}

class PolishDictionary : Handler
{
    override public void Translator(string word) 
    {
        switch (word)
        {
            case "Job":
                word = "Praca";
                break;
            case "Rest":
                word = "Odpoczynek";
                break;
        }
        Console.WriteLine("\t\tinto Polish => \"{0}\"", word);
    }
}
////
class Client
{
    static void Main()
    {
        Handler h1 = new FrenchDictionary();
        Handler h2 = new GermanDictionary();
        Handler h3 = new PolishDictionary();

        //Determining the consequences in the chain
        h1.First=h1;
        h1.Successor=h2;
        h2.Successor=h3;
        h3.Successor=null;

        //The word that is translated
        string request = "Job";

        //Starting the recursive method.
        h1.HandleRequest(request) ;

        //Another word is translated.
        request = "Rest";
        h1.HandleRequest(request);

        Console.ReadKey();
    }
}

/*output:

 We translate word => "Job"

    into French => "Travail"
    into German => "Arbeit"
    into Polish => "Praca"

 We translate word => "Rest"

    into French => "Reste"
    into German => "Rest"
    into Polish => "Odpoczynek"
 */



回答6:


You can find an example implementation of Chain of responsibility patter with lambda functions here Chain of responsibility - lambda function implementation .

It covers functionality of processing a Purchase Request through different actors - Manager,Director,President,Salesman.



来源:https://stackoverflow.com/questions/9194922/the-chain-of-responsibility-pattern

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!