Patrón iterador de serie de patrones de diseño: iteración sobre elementos en objetos agregados

Descripción: Design Patterns serie de artículos es leer 刘伟escrito 《设计模式的艺术之道(软件开发人员内功修炼之道)》un libro para leer notas. Personalmente, creo que este libro es muy bueno, y estoy interesado en leerlo. También puede consultar el blog del autor de este libro para obtener más detalles https://blog.csdn.net/LoveLion/article/details/17517213.

Resumen del modo

Definición del patrón

En el desarrollo de software, a menudo es necesario usar objetos agregados para almacenar una serie de datos. Los objetos agregados tienen dos responsabilidades:

  1. Almacenar datos
  2. Datos transversales

Desde la perspectiva de la dependencia, la primera es la responsabilidad básica de los objetos agregados, mientras que la segunda es variable y desmontable. Por lo tanto, a través del comportamiento de datos se pueden separar hacia fuera desde el objeto agregado, llamado empaquetado en un 迭代器sujeto, por el 迭代器acto de proporcionar los datos dentro del objeto de atravesar a la polimerización, lo que simplificará el diseño del objeto agregado, más consistente con un solo responsabilidad Requerimientos

Patrón de iterador ( Iterator Pattern): proporciona una forma de acceder a los objetos agregados sin exponer el almacenamiento interno del objeto. El patrón iterador es un patrón de comportamiento del objeto.

Diagrama de estructura del patrón

La estructura del patrón iterador contiene dos estructuras jerárquicas de agregación e iterador: considerando la flexibilidad y escalabilidad del sistema, el patrón se aplica en el 工厂方法patrón iterador y su estructura de patrón se muestra en la figura.

Diagrama de estructura de patrón de iterador

El diagrama de estructura del patrón iterador contiene los siguientes roles:

  • Iterator( 抽象迭代器): Define una interfaz para acceder y atravesar elementos, y declara métodos para atravesar elementos de datos, por ejemplo: un método para obtener el primer elemento, un first()método para acceder al siguiente elemento y un next()método para juzgar si todavía hay El hasNext()método del siguiente elemento, el método para obtener el elemento actual, currentItem()etc., estos métodos se implementarán en el iterador específico.
  • ConcreteIterator( 具体迭代器): Implementa la interfaz del iterador abstracto, completa el recorrido del objeto agregado y al mismo tiempo registra la posición actual en el objeto agregado a través del cursor en el iterador específico. En la implementación específica, el cursor suele ser una posición Entero no negativo.
  • Aggregate( 抽象聚合类): Se utiliza para almacenar y administrar objetos de elementos, declarar un createIterator()método para crear un objeto iterador y actuar como un rol de fábrica de iterador abstracto.
  • ConcreteAggregate( 具体聚合类): Implementa el createIterator()método declarado en la clase de agregación abstracta , que devuelve una ConcreteIteratorinstancia de iterador concreto correspondiente a la clase de agregación concreta .

Seudocódigo de patrón

En 抽象迭代器la polimerización se declaró método para atravesar los elementos de almacenado de objetos

interface Iterator {
	public void first(); //将游标指向第一个元素
	public void next(); //将游标指向下一个元素
	public boolean hasNext(); //判断是否存在下一个元素
	public Object currentItem(); //获取游标指向的当前元素
}

En 具体迭代器ealization 抽象迭代器método de los datos que atraviesan declarada

class ConcreteIterator implements Iterator {
	private ConcreteAggregate objects; //维持一个对具体聚合对象的引用,以便于访问存储在聚合对象中的数据
	private int cursor; //定义一个游标,用于记录当前访问位置
	public ConcreteIterator(ConcreteAggregate objects) {
		this.objects=objects;
	}
 
	public void first() {  ......  }
		
	public void next() {  ......  }
 
	public boolean hasNext() {  ......  }
	
	public Object currentItem() {  ......  }
}

聚合类Para almacenar datos 负责创建迭代器对象, el 抽象聚合类código más simple es el siguiente

interface Aggregate {
	Iterator createIterator();
}

La clase de agregación concreta es una subclase de la clase de agregación abstracta. Por un lado, es responsable del almacenamiento de datos. Por otro lado, el método de fábrica declarado en la clase de agregación abstracta se implementa createIterator()para devolver un objeto iterador específico correspondiente a la clase de agregación concreta. El código es el siguiente Se muestra

class ConcreteAggregate implements Aggregate {	
    //......	
    public Iterator createIterator() {
	      return new ConcreteIterator(this);
    }
	  //......
}

Mejoras de modo

En el diagrama de estructura del patrón iterador, podemos ver que hay una relación dual entre concreto 迭代器类y concreto 聚合类, una de las cuales es 关联关系que se 迭代器debe mantener una referencia a un objeto agregado específico en el concreto, el propósito de la relación de asociación es 访问存储在聚合对象中的数据que el iterador pueda Realice un recorrido en estos datos.

Además del uso 关联关系, para permitir que el iterador acceda a los datos en el objeto agregado, también podemos diseñar la clase iteradora como una clase agregada 内部类. La clase iteradora en el JDK se implementa mediante este método, como se muestra en el siguiente AbstractListfragmento de código Se muestra

package java.util;

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
  //...
  public boolean add(E e) {...}
  abstract public E get(int index);
  public E set(int index, E element) {
        throw new UnsupportedOperationException();
  }
  //...
  
  public Iterator<E> iterator() {
        return new Itr();
  }
  
  // 这里用内部类可直接访问到聚合对象中的数据
  private class Itr implements Iterator<E> {
       
        int cursor = 0;
        int lastRet = -1;
        
        int expectedModCount = modCount;

        public boolean hasNext() {
            return cursor != size();
        }

        public E next() {
            //...
        }

        public void remove() {
            //...
        }
    }
}

Aplicación de patrones

Aplicación de patrón en JDK

En el JDK, la Collectioninterfaz y la Iteratorinterfaz actúan como la capa de abstracción del patrón iterador, correspondiente a 抽象聚合类y respectivamente 抽象迭代器, y Collectionlas subclases de la interfaz actúan como se muestra en la 具体聚合类figura, que enumera algunas de las clases relacionadas con la Lista en el JDK y sus relaciones.

Diagrama de estructura de clase parcial en el marco de colección de Java

En el JDK, la situación real es mucho más complicado que en la figura, Listinterfaces de otros heredados Collectionlas interfaces iterator()a los métodos, sino que también añade un nuevo método de planta listIterator(), creado específicamente para ListIteratorel tipo de iterador, la Listsubclase LinkedListse implementa en este método, se puede usar Crear objetos de ListIteratorsubclase específicos ListItr.

Ahora que tenemos un iterator()método, ¿por qué necesitamos proporcionar un listIterator()método? ¿Habrá alguna duplicación de las funciones de estos dos métodos? ¿Por qué molestarse? Debido a que hay Iteratormuy pocos métodos definidos en la interfaz, solo hay tres, que solo se pueden realizar a través de estos tres métodos 正向遍历, y a veces necesitamos realizar una 逆向遍历operación en un objeto agregado , por lo que ListIteratorla hasPrevious()suma utilizada para el recorrido inverso se declara en la interfaz JDK previous()Para otros métodos, si el cliente necesita llamar a estos dos métodos para implementar el recorrido inverso, ya no puede usar el iterator()método para crear el iterador, porque el objeto iterador creado en este momento no tiene estos dos métodos. Leer masjava.util.ListIterator

Aplicación de patrones en proyectos de código abierto.

Cuando se usa el recorrido en proyectos de código abierto, los patrones de diseño de iterador se usan en muchos lugares. Solo mencione uno aquí org.apache.kafka.clients.consumer.ConsumerRecords. Los detalles se pueden encontrar en el kafka clientcódigo fuente para leer.

public class ConsumerRecords<K, V> implements Iterable<ConsumerRecord<K, V>> {
    @Override
    public Iterator<ConsumerRecord<K, V>> iterator() {
        return new ConcatenatedIterable<>(records.values()).iterator();
    }
    
    private static class ConcatenatedIterable<K, V> implements Iterable<ConsumerRecord<K, V>> {

        private final Iterable<? extends Iterable<ConsumerRecord<K, V>>> iterables;

        public ConcatenatedIterable(Iterable<? extends Iterable<ConsumerRecord<K, V>>> iterables) {
            this.iterables = iterables;
        }

        @Override
        public Iterator<ConsumerRecord<K, V>> iterator() {
            return new AbstractIterator<ConsumerRecord<K, V>>() {
                Iterator<? extends Iterable<ConsumerRecord<K, V>>> iters = iterables.iterator();
                Iterator<ConsumerRecord<K, V>> current;

                public ConsumerRecord<K, V> makeNext() {
                    while (current == null || !current.hasNext()) {
                        if (iters.hasNext())
                            current = iters.next().iterator();
                        else
                            return allDone();
                    }
                    return current.next();
                }
            };
        }
    }
}

Resumen de modo

El patrón iterador es un patrón de diseño utilizado con mucha frecuencia. Al introducir un iterador, la función de recorrido de datos se puede separar del objeto agregado. El objeto agregado solo es responsable de almacenar datos, y el iterador completa los datos de recorrido. Dado que muchas bibliotecas de clases de lenguaje de programación ya han implementado el modo iterador, en el desarrollo real, solo necesitamos usar directamente los iteradores que se han definido en lenguajes como Java y C #. Los iteradores se han convertido en lo básico para que podamos operar objetos agregados Una de las herramientas

  1. Principales ventajas

Las principales ventajas del patrón iterador son las siguientes:

(1) Admite atravesar un objeto agregado de diferentes maneras, y se pueden definir múltiples métodos de recorrido en el mismo objeto agregado. En el modo iterador, solo necesita reemplazar el iterador original con un iterador diferente para cambiar el algoritmo transversal. También podemos definir subclases del iterador para admitir el nuevo método transversal.

(2) El iterador simplifica la clase de agregación. Debido a la introducción de iteradores, no es necesario proporcionar métodos como el recorrido de datos en el objeto de agregación original, que pueden simplificar el diseño de la clase de agregación.

(3) En el modo de iterador, debido a la introducción de la capa de abstracción, es muy conveniente agregar nuevas clases de agregación y clases de iterador sin modificar el código original para cumplir con 开闭原则los requisitos.

(4) No importa cómo cambie la implementación, se puede usar Iterator. IteratorDespués de la introducción , el recorrido se puede separar de la implementación. Para el recorrido, solo podemos usar el método de hasNext()suma next(): si la estructura de almacenamiento de datos subyacente cambia (por ejemplo, Listse usa el almacenamiento original y se cambia el requisito para usar el Mapalmacenamiento), puede ser completamente transparente para la persona que llama. Al viajero no le importa cómo lo guarde.

  1. La principal desventaja

Las principales desventajas del patrón iterador son las siguientes:

(1) Debido a que el modo iterador separa la responsabilidad de almacenar datos y atravesarlos, agregar una nueva clase de agregación requiere agregar correspondientemente una nueva clase iteradora. El número de clases aumenta en pares, lo que aumenta la complejidad del sistema en cierta medida. .

(2) la dificultad de diseño de la abstracción iterador grande, necesidad de tener plenamente en cuenta la futura expansión del sistema, tales como el JDK iterador incorporada Iteratorrecorrido inverso no se puede lograr, si la necesidad de implementar un recorrido inverso, sólo a través de sus subclases ListIteratorpara poner en práctica, etc., y ListIteratorla iteración El controlador no puede usarse para Setobjetos agregados de tipo de operación . Al personalizar iteradores, no es fácil crear un iterador abstracto completo.

  1. Escena aplicable

El patrón iterador puede considerarse en las siguientes situaciones:

(1) Acceda al contenido de un objeto agregado sin exponer su representación interna. Separe el acceso de los objetos agregados del almacenamiento de datos internos, de modo que no necesite conocer los detalles de implementación internos al acceder a los objetos agregados.

(2) Es necesario proporcionar múltiples métodos de recorrido para un objeto agregado.

(3) Proporcionar una interfaz unificada para atravesar diferentes estructuras de agregación, y proporcionar diferentes métodos de recorrido para diferentes estructuras de agregación en la clase de implementación de la interfaz, y el cliente puede operar la interfaz de manera uniforme.

Supongo que te gusta

Origin www.cnblogs.com/itwild/p/12731108.html
Recomendado
Clasificación