Este artigo contén varias ligazóns externas e/ou bibliografía ao fin da páxina, mais poucas ou ningunha referencia no corpo do texto. Por favor, mellora o artigo introducindo notas ao pé, citando as fontes. Podes ver exemplos de como se fai nestes artigos. |
Este artigo ou sección precisa dunha revisión do formato que siga o libro de estilo da Galipedia. Pode axudar a mellorar este artigo e outros en condicións semellantes. |
Este artigo ou sección precisa dunha revisión ortográfica e/ou de gramática (recurso útil: corrector ortográfico en liña de galego). Podes axudarte do revisor ortográfico, activándoo en: Preferencias → Trebellos → Navegación → Ortografía: Activar o revisor ortográfico. Colabora connosco neste artigo e noutros en condicións semellantes para que a Galipedia mellore e medre. |
En programación orientada a obxecto, o padrón de deseño visitor (visitante) é unha forma de separar o algoritmo da estrutura dun obxecto.
É un padrón de comportamento, que permite incluír novos métodos nunha clase sen que se teña que modificar.
Representa unha operación sobre os elementos dunha estrutura de obxectos. Permítenos definir unha nova operación, ou modificala, sen cambiar as clases dos elementos sobre os que opera. Proporciona unha forma fácil e sostible de executar accións nunha familia de clases.
Un compilador representa os programas como árbores de sintaxe abstracta, sobre os que executa operacións. Moitas operacións necesitan diferenciar distintos tipos de nodo na árbore como poden ser expresións, variables etc.
O problema que nos atopamos é a dificultade de entender, manter, modificar e estender posto que cada clase ten a súa parte correspondente de cada unha das operacións.
Unha posible solución sería agrupar as operacións relacionadas de cada clase nun obxecto e pasarllo como argumento a cada nodo da árbore. A este obxecto chamariámoslle visitante (visitor) e o elemento que visita ten que “aceptalo” para que execute a operación para ese elemento. Na aceptación, o elemento envíalle ao visitante unha petición específica para a súa clase con el mesmo como argumento.
Se queremos definir máis dunha clase visitante, temos que crear unha superclase abstracta e definir unha operación por cada tipo de nodo.
Úsase o padrón Visitor cando:
O cliente visitará a cada elemento da estrutura de obxectos cun visitante concreto (previamente creado por el). Cando se visita un elemento, este chama á operación do visitante correspondente a súa clase. O obxecto pásase como argumento para permitir ó visitante o acceso ó seu estado.
Se nun programa usamos visitantes, é moi fácil engadir novas operacións, xa que o visitante contén o código en lugar de cada unha das subclases individuais. Ademais, os visitantes poden conter as operacións relacionadas nunha soa clase, e así non ten que obrigar a cambiar ou derivar clases para agregar estas operacións. Grazas a isto, o programa é máis sinxelo de escribir e manter.
Os padróns de deseño suxiren que o visitante pode proporcionar unha funcionalidade adicional a unha clase sen cambiala, pero é máis práctico dicir que un visitante pode agregar funcionalidade a unha colección de clases e encapsular os métodos que utiliza.
É difícil engadir novas clases de elementos, xa que obrigan a cambiar os visitantes.
Facilita a acumulación de estados, é dicir, acumular resultados.
No seguinte exemplo, teremos unha xerarquía de expresións aritméticas simples sobre as que se desexa definir visitantes. un dos visitantes será a operación PrettyPrit que converte a cadea de caracteres na expresión aritmética.
/*
* Esta é a superclase dunha xerarquía que permite representar expresións
* aritméticas simples e sobre a que desexamos definir visitantes.
*/
package expresion;
public abstract class Expresion {
abstract public void aceptar(VisitanteExpresion v);
}
package expresion;
public class Constante extends Expresion {
public Constante(int valor) { _valor = valor; }
public void aceptar(VisitanteExpresion v) { v.visitarConstante(this); }
int _valor;
}
package expresion;
public class Variable extends Expresion {
public Variable(String variable) { _variable = variable; }
public void aceptar(VisitanteExpresion v) { v.visitarVariable(this); }
String _variable;
}
package expresion;
public abstract class OpBinaria extends Expresion {
public OpBinaria(Expresion izq, Expresion der) { _izq = izq; _der = der; }
Expresion _izq, _der;
}
package expresion;
public class Suma extends OpBinaria {
public Suma(Expresion izq, Expresion der) { super(izq, der); }
public void aceptar(VisitanteExpresion v) { v.visitarSuma(this); }
}
package expresion;
public class Mult extends OpBinaria {
public Mult(Expresion izq, Expresion der) { super(izq, der); }
public void aceptar(VisitanteExpresion v) { v.visitarMult(this); }
}
/*
* Esta é a clase abstracta que define la interface dos visitantes
* da xerarquía Expresion -- en realidade, utilizamos una interface Java
* dado que tódolos métodos son abstractos.
*/
package expresion;
public interface VisitanteExpresion {
public void visitarSuma(Suma s);
public void visitarMult(Mult m);
public void visitarVariable(Variable v);
public void visitarConstante(Constante c);
}
/**
* Un dos posibles visitantes das Expresiones é un pretty printer
* que converte a cadea de caracteres a expresión aritmética. O algoritmo
* usado non optimiza o uso de parénteses... O resultado acumúlase no atributo privado
* _resultado, podéndose acceder a este dende o exterior mediante o método obtenerResultado()
*/
package expresion;
public class PrettyPrinterExpresion implements VisitanteExpresion {
// visitar a variable neste caso é gardar no resultado a variable
// asociada ó obxecto... Observe que accedemos ó estado interno do obxecto
// confiando na visibilidade de paquete...
public void visitarVariable(Variable v) {
_resultado = v._variable;
}
public void visitarConstante(Constante c) {
_resultado = String.valueOf(c._valor);
}
// Dado que o pretty-printer dunha operación binaria é case idéntica,
// podo factorizar parte do código con este método privado...
private void visitarOpBinaria(OpBinaria op, String pOperacion) {
op._izq.aceptar(this);
String pIzq = obtenerResultado();
op._der.aceptar(this);
String pDer = obtenerResultado();
_resultado = "(" + pIzq + pOperacion + pDer + ")";
}
// Por último a visita da suma e a mult resólvese mediante o método
// privado que se acaba de mencionar...
public void visitarSuma(Suma s) {
visitarOpBinaria(s, "+");
}
public void visitarMult(Mult m) {
visitarOpBinaria(m, "*");
}
// O resultado almacénase nun String privado. Proporciónase un método
// de acceso público para que os clientes do visitante poidan acceder
// ó resultado da visita
public String obtenerResultado() {
return _resultado;
}
private String _resultado;
}
import expresion.*;
class Main {
static public void main(String argv[]) {
// Construcción de una expresión (a+5)*(b+1)
Expresion expresion = new Mult( new Suma( new Variable("a"),
new Constante(5) ),
new Suma( new Variable("b"),
new Constante(1) ));
// Pretty-printing...
PrettyPrinterExpresion pretty = new PrettyPrinterExpresion();
expresion.aceptar(pretty);
// Visualizacion de resultados
System.out.println("Resultado: " + pretty.obtenerResultado());
}
}