The responsibility chain mode aims to organize an object chain to process a request such as a method call.
When ConcreteHandler (a specific Handler) does not know how to satisfy the request from the Client, or does not aim at this, it delegates it to the next Handler (Handler) in the chain).
This design pattern is usually used together with the composite pattern, where some leaf or container objects delegate operations to their parent objects by default. Another example is that localization is usually handled by the responsible chain. When the German translation adapter does not find a suitable result for the translation keyword, it returns to the English adapter or simply displays the keyword itself.
Coupling to a minimum: the Client class does not know which specific class is used to process requests; the link is configured when the object graph is created; the ConcreteHandlers does not know which object is their successor. The behavior is successfully allocated between objects. The most recent objects in the chain have priority and responsibility to meet the request.
Participants:
◆ Client: submit a request to Handler;
◆ Handler Abstraction: receives a request and satisfies it in some way;
◆ ConcreteHandlers (Specific handler): receives a request and tries to satisfy it. If it fails, delegates it to the next handler.
The following code implements the most famous example of a chain of responsibilities: multi-level cache.
Copy codeThe Code is as follows:
/**
* The Handler implements action. Objects that want to be a part of
* ChainOfResponsibility must implement this interface directly or
* Inheritance from an authentication acthandler.
*/
Interface KeyValueStore
{
/**
* Obtain a value.
* @ Param string $ key
* @ Return mixed
*/
Public function get ($ key );
}
/**
* Basic no-op implementation which ConcreteHandlers not interested in
* Caching or in interfering with the retrieval inherit from.
*/
Abstract class AbstractKeyValueStore implements KeyValueStore
{
Protected $ _ nextHandler;
Public function get ($ key)
{
Return $ this-> _ nextHandler-> get ($ key );
}
}
/**
* Ideally the last ConcreteHandler in the chain. At least, if inserted in
* A Chain it will be the last node to be called.
*/
Class SlowStore implements KeyValueStore
{
/**
* This cocould be a somewhat slow store: a database or a flat file.
*/
Protected $ _ values;
Public function _ construct (array $ values = array ())
{
$ This-> _ values = $ values;
}
Public function get ($ key)
{
Return $ this-> _ values [$ key];
}
}
/**
* A ConcreteHandler that handles the request for a key by looking for it in
* Its own cache. Forwards to the next handler in case of cache miss.
*/
Class InMemoryKeyValueStore implements KeyValueStore
{
Protected $ _ nextHandler;
Protected $ _ cached = array ();
Public function _ construct (KeyValueStore $ nextHandler)
{
$ This-> _ nextHandler = $ nextHandler;
}
Protected function _ load ($ key)
{
If (! Isset ($ this-> _ cached [$ key]) {
$ This-> _ cached [$ key] = $ this-> _ nextHandler-> get ($ key );
}
}
Public function get ($ key)
{
$ This-> _ load ($ key );
Return $ this-> _ cached [$ key];
}
}
/**
* A ConcreteHandler that delegates the request without trying
* Understand it at all. It may be easier to use in the user interface
* Because it can specialize itself by defining methods that generates
* Html, or by addressing similar user interface concerns.
* Some Clients see this object only as an instance of KeyValueStore
* And do not care how it satisfy their requests, while other ones
* May use it in its entirety (similar to a class-based adapter ).
* No client knows that a chain of Handlers exists.
*/
Class FrontEnd extends actkeyvaluestore
{
Public function _ construct (KeyValueStore $ nextHandler)
{
$ This-> _ nextHandler = $ nextHandler;
}
Public function getEscaped ($ key)
{
Return htmlentities ($ this-> get ($ key), ENT_NOQUOTES, 'utf-8 ');
}
}
// Client code
$ Store = new SlowStore (array ('pd '=> 'Philip K. Dick ',
'A' => 'isaac Asimov ',
'Ac' => 'Arthur C. Clarke ',
'Hh '=> 'helmut Hei encrypt enb U ttel '));
// In development, we skip cache and pass $ store directly to FrontEnd
$ Cache = new InMemoryKeyValueStore ($ store );
$ FrontEnd = new FrontEnd ($ cache );
Echo $ frontEnd-> get ('ia '), "\ n ";
Echo $ frontEnd-> getEscaped ('hh'), "\ n ";
Some implementation instructions on the PHP responsibility chain design model:
◆ The responsibility chain may already exist in the object diagram, which is the same as the example in the composite mode;
◆ In addition, Handler abstraction may or may not exist. The best choice is that a separate Handler interface can only perform the handleRequest () operation. Do not force a chain to be in only one layer, because the following already exists;
◆ An abstract class may also be introduced. However, because request processing is an orthogonal concern, the specific class may have inherited other classes;
◆ Constructor or setter, Handler (or the next Handler) is injected into the Client or the previous Handler;
◆ The request object is usually a ValueObject and may be implemented as a Flyweight. in PHP, it may be a scalar type, such as string. Note that in some languages, A string is a constant ValueObject.
A simple summary of the responsibility chain model can be summarized as: using a series of classes (classes) to try to process a request, these classes are loosely coupled, the only thing in common is passing requests between them. that is to say, when A request comes, Class A first processes it. If it is not processed, it is passed to Class B for processing. If it is not processed, it is passed to Class C for processing, it is passed down like a chain.