En el diseño orientado a objetos , el patrón de cadena de responsabilidad es un patrón de diseño de comportamiento que consta de una fuente de objetos de comando y una serie de objetos de procesamiento . [1] Cada objeto de procesamiento contiene una lógica que define los tipos de objetos de comando que puede manejar; el resto se pasa al siguiente objeto de procesamiento en la cadena. También existe un mecanismo para agregar nuevos objetos de procesamiento al final de esta cadena.
En una variación del modelo estándar de cadena de responsabilidades, algunos controladores pueden actuar como despachadores , capaces de enviar comandos en diversas direcciones, formando un árbol de responsabilidades . En algunos casos, esto puede ocurrir de forma recursiva, con objetos de procesamiento que llaman a objetos de procesamiento superiores con comandos que intentan resolver una parte más pequeña del problema; en este caso, la recursión continúa hasta que se procesa el comando o se ha explorado todo el árbol. Un intérprete XML podría funcionar de esta manera.
Este patrón promueve la idea de acoplamiento flexible .
El patrón de cadena de responsabilidad es estructuralmente casi idéntico al patrón de decorador , la diferencia es que para el decorador, todas las clases manejan la solicitud, mientras que para la cadena de responsabilidad, exactamente una de las clases en la cadena maneja la solicitud. Esta es una definición estricta del concepto de Responsabilidad en el libro de GoF . Sin embargo, muchas implementaciones (como los registradores a continuación, o el manejo de eventos de UI, o los filtros de servlets en Java, etc.) permiten que varios elementos en la cadena asuman la responsabilidad.
El patrón de diseño Cadena de Responsabilidad [2] es uno de los veintitrés patrones de diseño GoF conocidos que describen soluciones comunes a problemas de diseño recurrentes al diseñar software orientado a objetos flexible y reutilizable, es decir, objetos que son más fáciles de implementar, cambiar, probar y reutilizar.
Implementar una solicitud directamente dentro de la clase que envía la solicitud es inflexible porque acopla la clase a un receptor particular y hace imposible admitir múltiples receptores. [3]
Esto nos permite enviar una solicitud a una cadena de receptores sin tener que saber cuál de ellos la gestiona. La solicitud se transmite a lo largo de la cadena hasta que un receptor la gestiona. El remitente de una solicitud ya no está asociado a un receptor en particular.
Vea también el diagrama de clases y secuencias UML a continuación.
En el diagrama de clases UML anterior , la clase no hace referencia directamente a una clase receptora en particular. En cambio, hace referencia a la interfaz para manejar una solicitud ( ), lo que hace que la clase sea independiente de qué receptor maneja la solicitud. Las clases , y implementan la interfaz manejando o reenviando una solicitud (según las condiciones de tiempo de ejecución).
El diagrama de secuencia UML
muestra las interacciones en tiempo de ejecución: En este ejemplo, el objeto llama al objeto (de tipo ). El reenvía la solicitud a , que a su vez reenvía la solicitud a , que maneja (realiza) la solicitud.Sender
Sender
Handler
handler.handleRequest()
Sender
Receiver1
Receiver2
Receiver3
Handler
Sender
handleRequest()
receiver1
Handler
receiver1
receiver2
receiver3
Esta implementación de C++11 se basa en la implementación anterior a C++98 del libro. [5]
#include <iostream> #include <memoria> typedef int Tema ; constexpr Tema NO_HELP_TOPIC = -1 ; // define una interfaz para manejar solicitudes. class HelpHandler { // Handler public : HelpHandler ( HelpHandler * h = nullptr , Topic t = NO_HELP_TOPIC ) : successor ( h ), topic ( t ) {} virtual bool hasHelp () { return topic != NO_HELP_TOPIC ; } virtual void setHandler ( HelpHandler * , Topic ) {} virtual void handleHelp () { std :: cout << "HelpHandler::handleHelp \n " ; // (opcional) implementa el enlace sucesor. if ( successor != nullptr ) { successor -> handleHelp (); } } virtual ~ HelpHandler () = default ; HelpHandler ( const HelpHandler & ) = delete ; // regla de tres HelpHandler & operator = ( const HelpHandler & ) = delete ; private : HelpHandler * successor ; Topic topic ; }; clase Widget : público HelpHandler { público : Widget ( const Widget & ) = eliminar ; // regla de tres Widget & operador = ( const Widget & ) = eliminar ; protegido : Widget ( Widget * w , Tema t = NO_HELP_TOPIC ) : HelpHandler ( w , t ), padre ( nullptr ) { padre = w ; } privado : Widget * padre ; }; // maneja las solicitudes de las que es responsable. class Button : public Widget { // ConcreteHandler public : Button ( std :: shared_ptr < Widget > h , Topic t = NO_HELP_TOPIC ) : Widget ( h . get (), t ) {} virtual void handleHelp () { // si ConcreteHandler puede manejar la solicitud, lo hace; de lo contrario, reenvía la solicitud a su sucesor. std :: cout << "Button::handleHelp \n " ; if ( hasHelp ()) { // maneja las solicitudes de las que es responsable. } else { // puede acceder a su sucesor. HelpHandler :: handleHelp (); } } }; clase Dialog : public Widget { // ConcreteHandler public : Dialog ( std :: shared_ptr < HelpHandler > h , Topic t = NO_HELP_TOPIC ) : Widget ( nullptr ) { setHandler ( h . get (), t ); } virtual void handleHelp () { std :: cout << "Dialog::handleHelp \n " ; // Operaciones del widget que Dialog anula... if ( hasHelp ()) { // ofrecer ayuda sobre el diálogo } else { HelpHandler :: handleHelp (); } } }; clase Aplicación : público HelpHandler { público : Aplicación ( Tema t ) : HelpHandler ( nullptr , t ) {} virtual void handleHelp () { std :: cout << "Aplicación::handleHelp \n " ; // mostrar una lista de temas de ayuda } }; int main () { constexpr Tema TEMA_IMPRIMIR = 1 ; constexpr Tema TEMA_ORIENTACIÓN_PAPEL = 2 ; constexpr Tema TEMA_APLICACIÓN = 3 ; // Los punteros inteligentes evitan fugas de memoria. std :: shared_ptr < Aplicación > aplicación = std :: make_shared < Aplicación > ( TEMA_APLICACIÓN ); std :: shared_ptr < Diálogo > diálogo = std :: make_shared < Diálogo > ( aplicación , TEMA_IMPRIMIR ); std :: shared_ptr < Botón > botón = std :: make_shared < Botón > ( diálogo , TEMA_ORIENTACIÓN_PAPEL ); botón -> handleHelp (); }
Los frameworks Cocoa y Cocoa Touch , utilizados para aplicaciones OS X e iOS respectivamente, utilizan activamente el patrón de cadena de responsabilidad para gestionar eventos. Los objetos que participan en la cadena se denominan objetos respondedores y heredan de la clase NSResponder
(OS X)/ UIResponder
(iOS). Todos los objetos de vista ( NSView
/ UIView
), objetos de controlador de vista ( NSViewController
/ UIViewController
), objetos de ventana ( NSWindow
/ UIWindow
) y el objeto de aplicación ( NSApplication
/ UIApplication
) son objetos respondedores.
Normalmente, cuando una vista recibe un evento que no puede controlar, lo envía a su supervista hasta que llega al controlador de vista o al objeto de ventana. Si la ventana no puede controlar el evento, el evento se envía al objeto de aplicación, que es el último objeto de la cadena. Por ejemplo:
{{cite book}}
: CS1 maint: varios nombres: lista de autores ( enlace )