Chain of Responsibility

Em Orientação a Objetos, Chain of Responsibility é um padrão GOF cuja principal função é evitar a dependência entre um objeto receptor e um objeto solicitante. Consiste em uma série de objetos receptores e de objetos de solicitação, onde cada objetos de solicitação possui uma lógica interna que separa quais são tipos de objetos receptores que podem ser manipulados. O restante é passado para o próximo objetos de solicitação da cadeia.

Devido a isso, é um padrão que utiliza a ideia de baixo acoplamento por permitir que outros objetos da cadeia tenham a oportunidade de tratar uma solicitação.

Estrutura

editar

Passo-a-passo[1]

editar
  • A base mantém um ponteiro como "próximo";
  • Cada classe derivada implementa sua própria contribuição para manusear o pedido (request);
  • Se o pedido precisa ser passado para outra classe, então a classe derivada "chama de volta" à classe padrão, delegando um novo ponteiro;
  • O cliente (terceirizado ou não) cria e encadeia a cadeia, a qual pode incluir uma ligação do último nó até o nó da raiz;
  • O cliente lança e deixa cada request com a raiz da cadeia;
  • As delegações recursivas produzem um efeito de ilusão;

Situações de Uso

editar
  • Mais de um objeto pode tratar uma solicitação e o objeto que a tratará não é conhecido a priori;
  • O objeto que trata a solicitação deve ser escolhido automaticamente;
  • Deve-se emitir uma solicitação para um dentre vários objetos, sem especificar explicitamente o receptor;
  • O conjunto de objetos que pode tratar uma solicitação deveria ser especificado dinamicamente.

Em um sistema orientado a objetos esses interagem entre si através de mensagens, e o sistema necessita de determinar qual o objeto que irá tratar a requisição. O padrão de projeto Chain of Responsibility permite determinar quem será o objeto que irá tratar a requisição durante a execução. Cada objeto pode tratar ou passar a mensagem para o próximo na cascata.

Em um escritório, por exemplo, onde se tem 4 linhas telefônicas, a primeira linha é o primeiro objeto, a segunda linha é o segundo, e assim sucessivamente até a gravação automática que é o quinto objeto. Se a primeira linha estiver disponível ela irá tratar a ligação, se não ela passa a tarefa para o próximo objeto, que é a segunda linha. Se essa estiver ocupada ela passa a tarefa para a próxima e assim sucessivamente até que um objeto possa tratar a tarefa.

Nesse caso, se todas as linhas estiverem ocupadas o último objeto, que é a gravação automática, tratará da tarefa.

Exemplo

editar

Abaixo está um exemplo do padrão na linguagem de programação Java. Neste exemplo, temos atores diferentes, cada um estabelecendo um limite de compras à seu sucessor. Toda vez que um ator recebe um pedido de compra que exceda seu limite, o pedido é passado ao seu sucessor.

A classe abstrata PurchasePower com o método abstrato processRequest:

abstract class PurchasePower
{
    protected static final double BASE = 500;
    protected PurchasePower successor;

    abstract protected double getAllowable();

    abstract protected String getRole();

    public void setSuccessor(PurchasePower successor)
    {
        this.successor = successor;

    }

    public void processRequest(PurchaseRequest request)
    {
        if (request.getAmount() < this.getAllowable())
        {
            System.out.println(this.getRole() + " will approve $" + request.getAmount());
        }
        else if (successor != null)
        {
            successor.processRequest(request);
        }
    }
}

Temos quatro implementações da classe abstrata acima: Manager (Gerente), Director (Diretor), Vice President (Vice Presidente) e President (Presidente), cada uma multiplicando seu poder de compra:

class ManagerPPower extends PurchasePower
{
    protected double getAllowable()
    {
        return BASE*10;
    }

    protected String getRole()
    {
        return "Manager";
    }
}

class DirectorPPower extends PurchasePower
{
    protected double getAllowable()
    {
        return BASE*20;
    }

    protected String getRole()
    {
        return "Director";
    }
}

class VicePresidentPPower extends PurchasePower
{
    protected double getAllowable()
    {
        return BASE*40;
    }

    protected String getRole()
    {
        return "Vice President";
    }
}

class PresidentPPower extends PurchasePower
{
    protected double getAllowable()
    {
        return BASE*60;
    }

    protected String getRole()
    {
        return "President";
    }
}

O código abaixo define que a classe PurchaseRequest mantenha os dados em uma outra classe, como neste exemplo:

class PurchaseRequest
{

    private double amount;
    private String purpose;

    public PurchaseRequest(double amount, String purpose)
    {
        this.amount = amount;
        this.purpose = purpose;
    }

    public double getAmount()
    {
        return amount;
    }

    public void setAmount(double amt)
    {
        amount = amt;
    }

    public String getPurpose()
    {
        return purpose;
    }

    public void setPurpose(String reason)
    {
        purpose = reason;
    }
}

No uso que fizemos deste exemplo, temos a devida ordem (do menor poder de compra para o maior): Manager -> Director -> Vice President -> President

class CheckAuthority
{
    public static void main(String[] args)
    {
        ManagerPPower manager = new ManagerPPower();
        DirectorPPower director = new DirectorPPower();
        VicePresidentPPower vp = new VicePresidentPPower();
        PresidentPPower president = new PresidentPPower();
        manager.setSuccessor(director);
        director.setSuccessor(vp);
        vp.setSuccessor(president);

        // Press Ctrl+C to end.
        try
        {
            while (true)
            {
                System.out.println("Enter the amount to check who should approve your expenditure.");
                System.out.print(">");
                double d = Double.parseDouble(new BufferedReader(new InputStreamReader(System.in)).readLine());
                manager.processRequest(new PurchaseRequest(d, "General"));
            }
        } catch (Exception e)
        {
            System.exit(1);
        }
    }
}

Regras Importantes[1]

editar
  • Chain of Responsibility, Command, Mediator, e Observer são como endereços, podendo ser separados entre remetentes e destinatários, mas com consequências diferentes. O Chain of Responsibility passa um pedido ao remetente junto com uma cadeia de potenciais destinatários.
  • Chain of Responsibility pode usar o padrão Command para representar pedidos como objetos.
  • Chain of Responsibility é frequentemente aplicado em conjunto com o padrão Composite. Os componentes da classe-mãe podem ser usados para a classe-filha também.

Referências externas

editar
  1. a b «Design Patterns and Refactoring». sourcemaking.com. Consultado em 2 de outubro de 2016 

Ligações externas

editar
  Este artigo sobre programação de computadores é um esboço. Você pode ajudar a Wikipédia expandindo-o.