Padrão de Projeto Visitor Daniel Chevitarese Leandro Alvim Introdução • • • • • • • • Propósito do Padrão Motivação Aplicação Estrutura Participantes Colaborações Conseqüências Exemplo de Código Propósito do Padrão • Representação de operações; • Possibilitar a adição de operações especializadas de forma que não haja a necessidade da modificação de diversos elementos da estrutura Motivação • Problema – Representar operações especializadas que devem ser executadas sobre os elementos de uma estrutura • Solução usual – Implementar operação especializada em cada elemento da estrutura • Solução por padrão (Visitor) – Elaboração de um padrão que possibilite a adição de novas operações especializadas de forma que não haja a necessidade da modificação de diversos elementos da estrutura – Como ? • Separação do comportamento da estrutura Motivação • Exemplo de um Compilador – Representação de um programa • Árvore sintática abstrata – A árvore possui diferentes tipos de nós » Operadores; » Variáveis; » Expressões matemáticas; – Exemplo de operações de um compilador na árvore sintática abstrata • • • • Formatação; Verificação de tipos; Verificar se todas as variáveis estão definidas; Geração de código Motivação • Problema – As operações podem necessitar tratar cada tipo de nó de maneira diferente • Solução usual – Definição da operação na classe que a utiliza; – Desvantagens • Inclusão de novas operações requer a mudança de todas as classes de nós; • Poluição das classes com muitas operações – Mistura de verificação de tipos com formatação Motivação • Solução por padrão – Encapsular a operação em um objeto em separado (Visitor) – Algoritmo do Visitor • O objeto Visitor percorre os elementos da árvore; • Se um nó da árvore “aceita” um visitor então – chama um método seu que inclui o tipo do nó como argumento; – Executa a operação para aquele nó (a operação que costumava estar na classe nó) Motivação Aplicação • Quando se têm muitas classes de objetos com interfaces distintas e quer-se realizar operações nesses objetos que dependam das suas classes concretas; • Quando a estrutura de objetos é utilizada por diversas aplicações – Cada aplicação com seu Visitor apropriado; – Possibilidade de reuso de Visitors comuns; • Quando se quer evitar a poluição da classe com operações não-relacionadas e que são utilizadas em vários objetos; • Quando a estrutura que define os objetos é praticamente estática e as operações realizadas neles estão em constante mudança – A situação inversa não se aplica Estrutura Participantes • Visitor – Declara uma operação de visita (Visit) para cada classe a ser visitada (ConcreteElement); – Identifica a classe ConcreteElement através do nome e assinatura da operação Visit • ConcreteVisitor – Implementa cada operação de visita declarada pelo Visitor; – Cada operação implementará um algoritmo que receberá o seu contexto e estado de atuação • Element – Define a operação Accept que recebe um Visitor como argumento Participantes • ConcreteElement – Implementa a operação Accept que recebe um Visitor como argumento • ObjectStructure – Pode enumerar diversos Elements; – Pode prover uma interface que permita o Visitor visitar os seus Elements associados; – Pode ser um composite ou uma coleção (lista ou conjunto) Colaborações Conseqüências – Vantagens • Facilidade na adição de operações – Com as operações concentradas no Visitor, não há necessidade de implementação da operação em diversas classes; – Para uma nova funcionalidade, simplesmente adicione um novo Visitor • Agrupamento lógico-comportamental de operações – Comportamentos não ficam espalhados e sim agrupados por Visitor; – Comportamentos não relacionados ficam em Visitors distintos • Visitação de objetos pertencentes a hierarquias distintas – Note que um Iterador não conseguiria esta façanha • Acúmulo de estados – A não-utilização do Visitor, força o desenvolvedor a guardar o estado em um objeto e passando por parâmetro (padrão Collecting Parameter) Conseqüências – Desvantagens • A adição de classes ConcreteElement pode ser trabalhosa – Cada ConcreteVisitor tem que ser mudado para adicionar a nova operação – Dependência entre Visitante e Visitado • Quebra do encapsulamento – Como a operação encontra-se dentro de um Visitor e não em um ConcreteElement, este pode necessitar expor uma maior quantidade de interfaces para que o Visitor o acesse Exemplo de Código interface Visitor { void visit(Wheel wheel); void visit(Engine engine); void visit(Body body); void visitCar(Car car); void visitVehicle(Vehicle vehicle); } class Wheel { private String name; Wheel(String name) { this.name = name; } String getName() { return this.name; } public void accept(Visitor visitor) { visitor.visit(this); } } class Engine { public void accept(Visitor visitor) { visitor.visit(this); } } class Body { public void accept(Visitor visitor) { visitor.visit(this); } } abstract class Vehicle { protected Engine engine = new Engine(); protected Body body = new Body(); protected Wheel[] wheels; public Engine getEngine() { return this.engine; } public Body getBody() { return this.body; } public Wheel[] getWheels() { return this.wheels; } public void accept(Visitor visitor) { visitor.visitVehicle(this); } } class Car extends Vehicle { public Car() { super(); this.wheels = new Wheel[]{ new Wheel("front left"), new Wheel("front right"), new Wheel("back left"), new Wheel("back right") }; } public void accept(Visitor visitor) { visitor.visitCar(this); } } Exemplo de Código class PrintVisitor implements Visitor { class DoVisitor implements Visitor { public void visit(Wheel wheel) { public void visit(Wheel wheel) { System.out.println("Steering my wheel"); System.out.println("Visiting “ } + wheel.getName() public void visit(Engine engine) { + " wheel"); System.out.println("Starting my engine"); } } public void visit(Engine engine) { public void visit(Body body) { System.out.println("Visiting engine"); System.out.println("Moving my body"); } } public void visit(Body body) { public void visitCar(Car car) { System.out.println("Visiting body"); System.out.println("Starting my car"); } car.getEngine().accept(this); car.getBody().accept(this); public void visitVehicle(Vehicle vehicle) { for(Wheel wheel : car.getWheels()) { System.out.println("Visiting vehicle"); wheel.accept(this); } } public void visitCar(Car car) { } System.out.println("Visiting car"); public void visitVehicle(Vehicle vehicle) { car.getEngine().accept(this); System.out.println("Starting my vehicle"); car.getBody().accept(this); } for(Wheel wheel : car.getWheels()) { } wheel.accept(this); } } } Exemplo de Código public class VisitorDemo{ static public void main(String[] args){ Car car = new Car(); Visitor printVisitor = new PrintVisitor(); Visitor doVisitor = new DoVisitor(); car.accept(printVisitor); car.accept(doVisitor); } } Fim • Daniel Chevitarese – [email protected] • Leandro Alvim – [email protected]