Patrones de diseño: patrones de iterador

1. Introducción

En aplicaciones prácticas, tratamos de acceder a todos los elementos en un objeto agregado sin importar las condiciones internas de los elementos. Por ejemplo, un conductor de autobús vende boletos, no importa si la persona en el autobús es un hombre, una mujer, un anciano o un niño, siempre que suba al autobús, debe comprar un boleto. Esto corresponde al patrón de iterador.

2 Definiciones

Patrón de iterador: proporciona un objeto para acceder secuencialmente a una serie de datos en un objeto agregado sin exponer la representación interna del objeto agregado.

3 Estructura e Implementación

El patrón de iterador se implementa separando el comportamiento transversal del objeto agregado y abstrayéndolo en una clase de iterador. Su propósito es permitir que el código externo acceda de manera transparente a los datos internos del agregado sin exponer la estructura interna del objeto agregado.

El patrón de iterador incluye principalmente los siguientes roles:

  1. Función agregada abstracta: define la interfaz para almacenar, agregar, eliminar objetos agregados y crear objetos iteradores.
  2. Función Agregado concreto: implementa una clase agregada abstracta que devuelve una instancia de un iterador concreto.
  3. Rol de iterador abstracto (Iterator): define la interfaz para acceder y atravesar elementos agregados, que generalmente incluye hasNext(), first(), next() y otros métodos.
  4. Rol de iterador concreto: implementa los métodos definidos en la interfaz de iterador abstracto, completa el recorrido del objeto agregado y registra la posición actual del recorrido.

El diagrama de la estructura se muestra en la siguiente figura (la imagen es de la referencia 2):

4 Ventajas y desventajas 

4.1 Ventajas

  1. Acceda al contenido de un objeto agregado sin exponer su representación interna.
  2. La tarea transversal la realizan iteradores, lo que simplifica la clase de agregación.
  3. Admite atravesar un agregado de diferentes maneras, e incluso puede crear subclases de iteradores para admitir nuevos recorridos.
  4. Es muy conveniente agregar nuevas clases agregadas y clases iteradoras sin modificar el código original.
  5. Buena encapsulación, que proporciona una interfaz unificada para atravesar diferentes estructuras de agregación.

4.2 Desventajas

  1. Se aumenta el número de clases, lo que aumenta en cierta medida la complejidad del sistema.

5 escenarios de aplicación

  1. Cuando existe la necesidad de proporcionar múltiples formas de atravesar un objeto agregado.
  2. Cuando es necesario proporcionar una interfaz unificada para atravesar diferentes estructuras agregadas.
  3. Al acceder al contenido de un objeto agregado sin exponer la representación de sus detalles internos.

6 Explicación del código

En Java, Collection, List, Set, Map, etc., todos contienen iteradores. A continuación, echemos un vistazo a la implementación del iterador de List en Java.

6.1 Interfaz de iterador Iterador

public interface Iterator<E> {
    //获取下一个元素,第一次调用给出第一项,第二次给出第二项,。。。
     E next();
     //是否存在下一个,存在true,不存在false
     boolean hasNext();
     //从底层集合中删除迭代器返回的最后一个元素,就是next()返回的集合中的元素
     default void remove() {
        throw new UnsupportedOperationException("remove");
     }
     //对每个剩余的元素进行一定的操作,Consumer是函数式接口
     default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }

}

La función predeterminada en la interfaz es una característica recién introducida de Java8. El método modificado predeterminado solo se puede usar en la interfaz. El método marcado de forma predeterminada en la interfaz es un método común y el cuerpo del método se puede escribir directamente.

6.2 Implementación de Iterator en ArrayList

Obtener una instancia de iterador

public Iterator<E> iterator() {
        return new Itr();
    }

itr clase privada

private class Itr implements Iterator<E> {
        //下一个元素返回的索引
        int cursor;
        //最后一个元素的索引,没有为-1,获取实例Iterator时,为-1
        int lastRet = -1;
        // expectedModCount 预期集合元素被修改次数 modCount 集合被修改的数量
        int expectedModCount = modCount;

        Itr() {}

        /**
         * 判断元素
         * @return 是否还有元素
         */
        public boolean hasNext() {
            return cursor != size;
        }

        //返回下一个元素
        @SuppressWarnings("unchecked")
        public E next() {
            // 关键一步,调用checkForComodification()会去校验expectedModCount 和modCount是否相等,不等抛异常
            //集合元素被修改一次,modCount自加一次
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                //修改 让预期修改次数与实际修改次数相等
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

ArrayList.this.remove(lastRet)

 public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

6.3 Prueba de función principal

public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(i + 1);
        }
        Iterator iterator = list.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
            iterator.remove();
        }
    }

6.4 Resultados de la prueba de depuración

1. Después de agregar elementos a la lista, se obtiene una instancia de Iterator. Preste atención al valor del cursor y al valor de lastRet.

2. Después de eliminar un elemento, el iterador cambia.

3. Resultado de salida final

6.5 Resumen

Esta sección analiza la implementación de Iterator en ArrayList para ilustrar el diseño y la aplicación del patrón de iterador. Esto está en línea con nuestros hábitos de programación reales. En el desarrollo diario, apenas escribimos iteradores nosotros mismos. A menos que necesite personalizar un iterador correspondiente a una estructura de datos implementada por usted mismo, la API proporcionada por el marco de código abierto es completamente suficiente.

7 citas

1. "Patrones de diseño Dahua"

2. Modo iterador (versión detallada)

3. Argumento de que la colección no se puede modificar cuando se usa un iterador para recorrer la colección

8 código fuente

Debido a que la explicación es el código fuente de ArrayList en Java, no hay código fuente para este patrón de diseño.

Supongo que te gusta

Origin blog.csdn.net/honor_zhang/article/details/120966930
Recomendado
Clasificación