Iterador (padrón de deseño)

En deseño de software, o padrón de deseño Iterador, define unha interface que declara os métodos necesarios para acceder secuencialmente a un grupo de obxectos dunha colección. Algúns dos métodos que podemos definir na interface Iterador son:

primeiro(), seguinte(), haiMais() e elementoActual().

Este padrón de deseño permite percorrer unha estrutura de datos sen que sexa necesario coñecer a estrutura interna da mesma.

Propósito editar

O padrón Iterador é un mecanismo de acceso ós elementos que constitúen unha estrutura de datos para a utilización destes sen expoñer a súa estrutura interna.

Motivación editar

O padrón xorde do desexo de acceder ós elementos dun contedor de obxectos (por exemplo, unha lista) sen expoñer a súa representación interna. Ademais, é posible que se necesite máis dunha forma de percorrer a estrutura sendo para elo necesario crear modificacións na clase.

A solución que propón o padrón é engadir métodos que permitan percorrer a estrutura sen referenciar explicitamente a súa representación. A responsabilidade do percorrido trasladase a un obxecto iterador.

O problema de introducir este obxecto iterador reside en que os clientes necesitan coñecer a estrutura para crear o iterador apropiado.

Isto solucionase xeneralizando os distintos iteradores nunha abstracción e dotando ás estruturas de datos dun método de fabricación que cree un iterador concreto.

 

Aplicabilidade editar

O padrón iterator permite o acceso ó contido dunha estrutura sen expoñer a súa representación interna. Ademais diferentes iteradores poden presentar diferentes tipos de percorrido sobre a estrutura (percorrido de principio a fin, percorrido con saltos...). Por outro lado os iteradores non teñen por qué limitarse a percorrer a estrutura, senón que poderían incorporar outro tipo de lóxica (por exemplo, filtrado de elementos). É máis, dados diferentes tipos de estruturas, o padrón iterador permite percorrelas todas utilizando unha interface común uniforme.

Estrutura editar

 

Participantes editar

As entidades participantes no deseño proposto polo padrón iterador son:

  • Iterador (Iterator): define a interface para percorrer o agregado de elementos e acceder a eles, de maneira que o cliente non teña que coñecer os detalles e sexa capaz de manexalos de todos modos.
  • Iterador Concreto (ConcreteIterator): implementa a interface proposta polo Iterador. É o que se encarga de manter a posición actual no percorrido da estrutura.
  • Agregado (Aggregate): define a interface para o método de fabricación de iteradores.
  • Agregado Concreto (ConcreteAggregate): implementa a estrutura de datos e o método de fabricación de iteradores que crea un iterador específico para a súa estrutura.

Colaboracións editar

Un iterador concreto é o encargado de gardar a posición actual dentro da estrutura de datos, interactuando con esta para calcular o seguinte elemento do percorrido.

Consecuencias editar

O padrón Iterador permite por tanto diferentes tipos de percorrido dun agregado e varios percorridos simultáneos, simplificando a interface do agregado.

Implementación editar

Para a creación do padrón iterador debe implementarse o control da iteración e definirse o percorrido. O control da iteración pode realizarse de dúas formas:

  • Iterador externo: ofrece os métodos para que o cliente percorra a estrutura paso a paso (primeiro(), seguinte(), haiMais() e elementoActual()).
  • Iterador interno: ofrece un método de actuación sobre a estrutura que, de maneira transparente ó cliente, realiza o percorrido de todos os seus elementos aplicando unha determinada operación sobre eles.

A maiores poderíanse implementar operacións adicionais no iterador o definir a estrutura deste dunha maneira máis robusta ante cambios na estrutura. Hai que ter especial coidado na implementación de iteradores con accesos privilexiados, iteradores para estruturas compostas ou iteradores nulos.

Exemplo en Java do código dun iterador editar

Definición do agregado concreto.

/**
 * Clase que implementa o comportamento dun vector de enteiros.
 */
public class Vector2 
{
    //Array de enteiro onde se almacenaran os datos dos elementos do vector.
    public int[] _datos;

    /**
     * Método constructor da clase Vector2.
     *
     * @param valores Número de elementos que poderán ser almacenados no vector.
     */
    public Vector2(int valores)
    { 
        _datos = new int[valores];
        
        //Inicializamos a cero todos os elementos do vector.
        for (int i = 0; i < _datos.length; i++)
        {
            _datos[i] = 0; 
        }
    }    
    
    /**
     * Método para obter o valor dunha posición do vector.
     *
     * @param pos A posición sobre a que se quere obter o valor.
     * @return O valor que conten o vector na posición indicada como parámetro.
     */
    public int obterValor(int pos)
    { 
        return _datos[pos]; 
    }

    /**
     * Método para asignar un valor a unha posición do vector.
     *
     * @param pos A posición a que se lle quere asignar o valor.
     * @param valor O valor que se quere asignar.
     */
    public void asignarValor(int pos, int valor)
    { 
        _datos[pos] = valor; 
    }

    /**
     * Método para obter o tamaño do vector.
     *
     * @return O tamaño do vector.
     */
    public int dimension()
    { 
        return _datos.length; 
    }
    
    /**
     * Método para obter un interador deste vector.
     *
     * @return Un iterador que permite percorrer o contido deste vector.
     */
    public IteradorVector iterador()
    {
        return new IteradorVector(this); 
    }
}

Definición do iterador concreto.

/**
 * Clase que permite percorrer o contido dunha instancia da clase Vector2.
 */
public class IteradorVector
{
    //Array para acceder o contido do vector.
    private int[] _vector;

    //Posición actual na que se atopa o iterador.
    private int _posicion;
 	
    /**
     * Método constructor da clase IteradorVector. 
     * Permite crear un iterador a partir dun Vector2.
     */
    public IteradorVector(Vector2 vector) 
    {
        _vector = vector._datos;

        //O percorrido que realizará o iterador será ascendente polo que inicializamos a posición a 0.
        _posicion = 0; 
    }
 	 	
    /**
     * Método que permite comprobar se hai máis elementos sen percorrer.
     *
     * @return True se hai máis elementos sen percorrer, false en caso contrario.
     */
    public boolean haiMais()
    {
        if (_posicion < _vector.length) 
		 return true;
	 else
		 return false;
    }

    /**
     * Método que devolve o seguinte elemento do vector que percorre este iterador.
     * 
     * @return O valor do seginte elemento do vector.     
     */
    public Object seguinte()
    {
	 int valor = _vector[_posicion];
	 _posicion++;
	 return valor;
    }
}

Exemplo de uso:

    //Método para probar o uso do noso iterador.
    public static void main(String argv[]) 
    {
        //Creamos un vector de 5 elementos.
    	Vector2 vector = new Vector2(5);
    	
    	//Asignamos valores ós elementos do vector.
    	vector.asignarValor(0, 3);
    	vector.asignarValor(2, 9);    	
    	vector.asignarValor(4, 1);    	
    	
    	//Creación do iterador.
    	IteradorVector iterador = vector.iterador();
    	
    	//Percorrido empregando o iterador.
    	while(iterador.hasNext())
            System.out.println(iterador.next());    	    	
    }

En java xa existe unha interface Iterator que fai ás veces de abstracción. Tódolos iteradores utilizados nas súas librerías cumpren esta interface, o que permite tratalos a todos eles de maneira uniforme.

Facer que o noso IteradorVector implementase Iterator permitiría que fose utilizado por outras librerías e programas de maneira transparente.

import java.util.Iterator;

/**
 * Clase que permite percorrer o contido dunha instancia da clase Vector2.
 * Esta clase implementa a interface Iterator de Java.
 */
public class IteradorVector implements Iterator
{
    //Array para acceder o contido do vector.
    private int[] _vector;

    //Posición actual na que se atopa o iterador.
    private int _posicion;

    /**
     * Método constructor da clase IteradorVector. 
     * Permite crear un iterador a partir dun Vector2.
     */
    public IteradorVector(Vector2 vector) 
    {
        _vector = vector._datos;

        //O percorrido que realizará o iterador será ascendente polo que inicializamos a posición a 0.
        _posicion = 0; 
    }

    /**
     * Método que permite comprobar se hai máis elementos sen percorrer.
     *
     * @return True se hai máis elementos sen percorrer, false en caso contrario.
     */
    public boolean hasNext()
    {
        if (_posicion < _vector.length)
            return true;
        else
            return false;
    }

    /**
     * Método que devolve o seguinte elemento do vector que percorre este iterador.
     * 
     * @return O valor do seguinte elemento do vector.
     */
    public Object next()
    {
        int valor = _vector[_posicion];
        _posicion++;
        return valor;
    }

    /**
     * Método para elimina do vector o último elemento devolto por este iterador.
     * A implementación deste método é opcional. Neste caso decidiuse lanzar unha excepción de operación non soportada.
     */
    public void remove()
    {
        throw new UnsupportedOperationException("Operación non soportada.");
    }
}