1. O que é o Padrão Chain of Responsibility
O Padrão Chain of Responsibility é um padrão de design comportamental que desacopla o remetente e o receptor de uma solicitação, permitindo que vários objetos tenham a chance de lidar com a solicitação. Cada receptor contém uma referência a outro receptor e, se não puder lidar com a solicitação, encaminha a solicitação para o próximo receptor até que a solicitação seja tratada ou atinja o final da cadeia.
2. Características e Vantagens do Padrão Chain of Responsibility
As características e vantagens do Padrão Chain of Responsibility são as seguintes:
- Desacoplamento de remetente e receptor: O remetente não precisa se importar com qual receptor lida com a solicitação, nem precisa saber os manipuladores específicos na cadeia.
- Flexibilidade: Permite a adição, remoção ou reordenação dinâmica de manipuladores na cadeia sem modificar o código do remetente e do receptor.
- Extensibilidade: Fácil de estender a cadeia de responsabilidade adicionando novos manipuladores específicos.
- Princípio da Responsabilidade Única: Cada manipulador específico só precisa se preocupar com sua própria lógica de tratamento.
- Configurabilidade: A cadeia de manipuladores pode ser configurada de acordo com as necessidades, permitindo que diferentes solicitações tenham diferentes cadeias de manipuladores.
3. Exemplos de Aplicações Práticas do Padrão Chain of Responsibility
O Padrão Chain of Responsibility tem muitas aplicações práticas, tais como:
- Manipulação de solicitações em aplicações web: Pode ser usado para lidar com diferentes tipos de solicitações, como autenticação de identidade, registro e verificação de permissão.
- Manipulação de erros: Pode ser usado para lidar com erros, sendo cada manipulador responsável por lidar com um tipo específico de erro e encaminhando o erro para o próximo manipulador, conforme necessário.
- Manipulação de eventos: Pode ser usado para lidar com diferentes tipos de eventos, como eventos de clique do usuário, eventos de solicitação de rede, e assim por diante.
4. Implementação do Padrão Chain of Responsibility em Golang
4.1 Diagrama de Classe UML
4.2 Introdução do Exemplo
No diagrama de classe UML acima, definimos um manipulador abstrato (Handler) e dois manipuladores concretos (ConcreteHandler1 e ConcreteHandler2). O cliente (Client) inicia solicitações chamando o método handleRequest do manipulador.
4.3 Passo 1 da Implementação: Definir a Interface do Manipulador Abstrato
type Handler interface {
HandleRequest(request Request) error
SetNext(handler Handler)
}
type Request interface {
Condition bool
}
A interface do manipulador abstrato define o método HandleRequest para processar solicitações e o método SetNext para definir o próximo manipulador.
4.4 Passo 2 da Implementação: Implementar Classes de Manipulador Concreto
type ConcreteHandler1 struct {
next Handler
}
func (h *ConcreteHandler1) HandleRequest(request Request) error {
// Lógica para lidar com a solicitação
if request.Condition {
// Código para lidar com a solicitação
return nil
} else {
if h.next != nil {
return h.next.HandleRequest(request)
}
return errors.New("Nenhum manipulador encontrado")
}
}
func (h *ConcreteHandler1) SetNext(handler Handler) {
h.next = handler
}
type ConcreteHandler2 struct {
next Handler
}
func (h *ConcreteHandler2) HandleRequest(request Request) error {
// Lógica para lidar com a solicitação
if request.Condition {
// Código para lidar com a solicitação
return nil
} else {
if h.next != nil {
return h.next.HandleRequest(request)
}
return errors.New("Nenhum manipulador encontrado")
}
}
func (h *ConcreteHandler2) SetNext(handler Handler) {
h.next = handler
}
As classes de manipulador concreto implementam a interface do manipulador abstrato e substituem os métodos HandleRequest e SetNext.
4.5 Passo 3 da Implementação: Construir a Cadeia de Responsabilidade
handler1 := &ConcreteHandler1{}
handler2 := &ConcreteHandler2{}
handler1.SetNext(handler2)
Ao instanciar os manipuladores concretos e definir o próximo manipulador, uma cadeia de responsabilidade é construída.
4.6 Passo 4 da Implementação: Código do Cliente
func main() {
handler := &ConcreteHandler1{}
// Construindo a cadeia de responsabilidade
handler.SetNext(&ConcreteHandler2{})
// Enviando uma solicitação
handler.HandleRequest(Request{Condition: true})
}
No código do cliente, um manipulador concreto é instanciado, o próximo manipulador é definido e, em seguida, o método HandleRequest é chamado para enviar uma solicitação.