La belleza de los patrones de diseño 69-Modo de visitante (Parte 2): ¿Por qué un idioma que admite envío doble no necesita un modo de visitante?

69|Patrón de visitantes (Parte 2): ¿Por qué un idioma que admite envío doble no necesita un patrón de visitantes?

En la última clase, aprendimos el principio y la implementación del patrón de visitante y restauramos el proceso de pensamiento del nacimiento del patrón de visitante. En términos generales, la implementación del código de este modo es relativamente difícil, por lo que no hay muchos escenarios de aplicación. Desde la perspectiva del desarrollo de aplicaciones, de hecho no es el foco de nuestro estudio.

Sin embargo, hemos dicho repetidamente antes que estudiar mi columna no es solo para que domines el conocimiento, sino que, lo que es más importante, es para ejercitar tu capacidad de análisis y resolución de problemas, y para ejercitar tu capacidad de pensamiento lógico. use el modo de visitante Como introducción, analicemos estos dos temas juntos, con la esperanza de inspirar su pensamiento profundo:

  • ¿Por qué un idioma que admite envío doble no necesita el patrón Visitor?
  • Además del patrón de visitante, ¿hay alguna otra forma de implementar el ejemplo de la lección anterior?

Sin más preámbulos, ¡comencemos oficialmente el estudio de hoy!

¿Por qué un idioma que admite envío doble no necesita el patrón Visitor?

De hecho, cuando se trata del patrón de visitantes, la mayoría de los libros o materiales hablarán de Double Dispatch, que se traduce como doble envío en chino. Aunque no es necesario comprender este concepto para aprender el modo visitante, no lo mencionamos en la explicación anterior, pero para evitar que se quede atascado en este concepto al ver otros libros o materiales, creo que es necesario Hablemos de eso aquí.

Además, creo que aprender Double Dispatch también puede profundizar su comprensión del patrón de visitantes y también puede ayudarlo a resolver la pregunta en el título del artículo de hoy: ¿Por qué los idiomas que admiten el envío doble no necesitan el patrón de visitantes? ? ? ¡Esta pregunta se hará durante la entrevista!

Dado que hay Despacho Doble, hay un Despacho Único correspondiente. El llamado envío único se refiere a qué método de objeto ejecutar se determina de acuerdo con el tipo de tiempo de ejecución del objeto; qué método ejecutar en el objeto se determina de acuerdo con el tipo de tiempo de compilación del parámetro del método. El llamado envío doble se refiere al método con el que se ejecuta el objeto, que se determina según el tipo de tiempo de ejecución del objeto; qué método del objeto a ejecutar se determina según el tipo de tiempo de ejecución del parámetro del método.

¿Cómo entender la palabra "Despacho"? En los lenguajes de programación orientados a objetos, podemos entender las llamadas a métodos como una especie de paso de mensajes, que es "Dispatch". Cuando un objeto llama a un método de otro objeto, es equivalente a enviarle un mensaje. Este mensaje debe contener al menos el nombre del objeto, el nombre del método y los parámetros del método.

¿Cómo entender las palabras "Simple" y "Doble"? "Simple" y "Doble" se refieren a qué método de qué objeto se ejecuta, lo que está relacionado con el tipo de tiempo de ejecución de varios factores. Vamos a explicar más. La razón por la que Single Dispatch se llama "Single" es que qué método de qué objeto se ejecuta solo está relacionado con el tipo de tiempo de ejecución del "objeto". La razón por la que Double Dispatch se llama "Doble" es porque qué método de qué objeto se ejecuta está relacionado con el tipo de tiempo de ejecución tanto del "objeto" como del "parámetro del método".

Específicos del mecanismo gramatical de los lenguajes de programación, Single Dispatch y Double Dispatch están directamente relacionados con el polimorfismo y la sobrecarga de funciones. Los principales lenguajes de programación orientados a objetos actuales (por ejemplo, Java, C ++, C #) solo admiten Single Dispatch, no Double Dispatch.

A continuación, tomemos el lenguaje Java como ejemplo.

Java admite polimorfismo.El código puede obtener el tipo real del objeto en tiempo de ejecución (es decir, el tipo de tiempo de ejecución mencionado anteriormente) y luego decidir qué método llamar en función del tipo real. Aunque Java admite la sobrecarga de funciones, la regla gramatical de la sobrecarga de funciones diseñada por Java no es decidir qué función sobrecargada llamar según el tipo real del parámetro pasado a la función en tiempo de ejecución, sino en tiempo de compilación, según el tipo declarado de el parámetro pasado a la función (es decir, el tipo de tiempo de compilación mencionado anteriormente) determina qué función sobrecargada llamar. Es decir, qué método de qué objeto se ejecuta solo está relacionado con el tipo de tiempo de ejecución del objeto y no tiene nada que ver con el tipo de tiempo de ejecución del parámetro. Por lo tanto, el lenguaje Java solo es compatible con Single Dispatch.

Esto es más abstracto, déjame darte un ejemplo para explicarlo de forma concreta, el código es el siguiente:

public class ParentClass {
  public void f() {
    System.out.println("I am ParentClass's f().");
  }
}

public class ChildClass extends ParentClass {
  public void f() {
    System.out.println("I am ChildClass's f().");
  }
}

public class SingleDispatchClass {
  public void polymorphismFunction(ParentClass p) {
    p.f();
  }

  public void overloadFunction(ParentClass p) {
    System.out.println("I am overloadFunction(ParentClass p).");
  }

  public void overloadFunction(ChildClass c) {
    System.out.println("I am overloadFunction(ChildClass c).");
  }
}

public class DemoMain {
  public static void main(String[] args) {
    SingleDispatchClass demo = new SingleDispatchClass();
    ParentClass p = new ChildClass();
    demo.polymorphismFunction(p);//执行哪个对象的方法,由对象的实际类型决定
    demo.overloadFunction(p);//执行对象的哪个方法,由参数对象的声明类型决定
  }
}

//代码执行结果:
I am ChildClass's f().
I am overloadFunction(ParentClass p).

En el código anterior, la función polymorphismFunction() en la línea 31 ejecuta la función f() del tipo real de p, que es la función f() de ChildClass. La función de sobrecarga () en la línea 32 de código coincide con la función de sobrecarga (ParentClass p) en la función sobrecargada, que es para decidir qué función sobrecargada coincidir de acuerdo con el tipo declarado de p.

Suponiendo que el lenguaje Java admita Double Dispatch, la línea 37 del siguiente código (extraído del segundo código de la lección anterior, se recomienda entenderlo junto con la explicación de la lección anterior) no informará un error. El código decidirá cuál de las tres funciones sobrecargadas de extract2txt usar según el tipo real (PdfFile, PPTFile, WordFile) del parámetro (resourceFile) en tiempo de ejecución. Luego, la siguiente implementación de código puede ejecutarse normalmente y no es necesario el modo de visitante. Esto también responde por qué un idioma que admite Double Dispatch no necesita el patrón Visitor.

public abstract class ResourceFile {
  protected String filePath;
  public ResourceFile(String filePath) {
    this.filePath = filePath;
  }
}

public class PdfFile extends ResourceFile {
  public PdfFile(String filePath) {
    super(filePath);
  }
  //...
}
//...PPTFile、WordFile代码省略...
public class Extractor {
  public void extract2txt(PPTFile pptFile) {
    //...
    System.out.println("Extract PPT.");
  }

  public void extract2txt(PdfFile pdfFile) {
    //...
    System.out.println("Extract PDF.");
  }

  public void extract2txt(WordFile wordFile) {
    //...
    System.out.println("Extract WORD.");
  }
}

public class ToolApplication {
  public static void main(String[] args) {
    Extractor extractor = new Extractor();
    List<ResourceFile> resourceFiles = listAllResourceFiles(args[0]);
    for (ResourceFile resourceFile : resourceFiles) {
      extractor.extract2txt(resourceFile);
    }
  }

  private static List<ResourceFile> listAllResourceFiles(String resourceDirectory) {
    List<ResourceFile> resourceFiles = new ArrayList<>();
    //...根据后缀(pdf/ppt/word)由工厂方法创建不同的类对象(PdfFile/PPTFile/WordFile)
    resourceFiles.add(new PdfFile("a.pdf"));
    resourceFiles.add(new WordFile("b.word"));
    resourceFiles.add(new PPTFile("c.ppt"));
    return resourceFiles;
  }
}

Además del patrón de visitante, ¿existen otras implementaciones para el ejemplo de la sección anterior?

En la última clase, usé un ejemplo para mostrarles cómo se diseña el patrón de visitantes paso a paso. Repasemos ese ejemplo nuevamente juntos. Rastreamos muchos archivos de recursos del sitio web y hay tres formatos: PDF, PPT, Word. Necesitamos desarrollar una herramienta para procesar este lote de archivos de recursos, lo que incluye extraer contenido de texto, comprimir archivos de recursos, extraer metainformación de archivos, etc.

De hecho, hay muchas ideas de diseño e implementación de código para desarrollar esta herramienta. Para explicar el patrón de visitante, decidimos implementarlo con el patrón de visitante en la última lección. De hecho, tenemos otros métodos de implementación, por ejemplo, también podemos usar el patrón de fábrica para definir una interfaz Extractor que incluye la función de interfaz extract2txt(). Las clases PdfExtractor, PPTExtractor y WordExtractor implementan la interfaz Extractor e implementan la extracción de contenido de texto de archivos en formato Pdf, PPT y Word en sus respectivas funciones extract2txt(). La clase de fábrica ExtractorFactory devuelve diferentes extractores según los diferentes tipos de archivos.

Esta idea de implementación es en realidad más simple, veamos el código directamente.

public abstract class ResourceFile {
  protected String filePath;
  public ResourceFile(String filePath) {
    this.filePath = filePath;
  }
  public abstract ResourceFileType getType();
}

public class PdfFile extends ResourceFile {
  public PdfFile(String filePath) {
    super(filePath);
  }

  @Override
  public ResourceFileType getType() {
    return ResourceFileType.PDF;
  }

  //...
}

//...PPTFile/WordFile跟PdfFile代码结构类似,此处省略...

public interface Extractor {
  void extract2txt(ResourceFile resourceFile);
}

public class PdfExtractor implements Extractor {
  @Override
  public void extract2txt(ResourceFile resourceFile) {
    //...
  }
}

//...PPTExtractor/WordExtractor跟PdfExtractor代码结构类似,此处省略...

public class ExtractorFactory {
  private static final Map<ResourceFileType, Extractor> extractors = new HashMap<>();
  static {
    extractors.put(ResourceFileType.PDF, new PdfExtractor());
    extractors.put(ResourceFileType.PPT, new PPTExtractor());
    extractors.put(ResourceFileType.WORD, new WordExtractor());
  }

  public static Extractor getExtractor(ResourceFileType type) {
    return extractors.get(type);
  }
}

public class ToolApplication {
  public static void main(String[] args) {
    List<ResourceFile> resourceFiles = listAllResourceFiles(args[0]);
    for (ResourceFile resourceFile : resourceFiles) {
      Extractor extractor = ExtractorFactory.getExtractor(resourceFile.getType());
      extractor.extract2txt(resourceFile);
    }
  }

  private static List<ResourceFile> listAllResourceFiles(String resourceDirectory) {
    List<ResourceFile> resourceFiles = new ArrayList<>();
    //...根据后缀(pdf/ppt/word)由工厂方法创建不同的类对象(PdfFile/PPTFile/WordFile)
    resourceFiles.add(new PdfFile("a.pdf"));
    resourceFiles.add(new WordFile("b.word"));
    resourceFiles.add(new PPTFile("c.ppt"));
    return resourceFiles;
  }
}

Cuando es necesario agregar nuevas funciones, como comprimir archivos de recursos, implementar código similar a la función de extraer contenido de texto, solo necesitamos agregar una interfaz de Compressor, PdfCompressor, PPTCompressor, WordCompressor tres clases de implementación y crear su clase de fábrica CompressorFactory . . Lo único que debe modificarse es la clase ToolApplication de nivel superior. Básicamente se ajusta al principio de diseño de "abierto a la extensión, cerrado a la modificación".

Para el ejemplo de una herramienta de procesamiento de archivos de recursos, si la herramienta no proporciona muchas funciones, pero solo unas pocas, entonces recomiendo usar la implementación del modo de fábrica, después de todo, el código es más claro y más fácil de entender. Por el contrario, si la herramienta proporciona muchas funciones, como más de una docena, entonces recomiendo usar el modo de visitante, porque el modo de visitante necesita definir muchas menos clases que la implementación del modo de fábrica, y demasiadas clases también afectar la mantenibilidad del Código.

revisión clave

Bueno, eso es todo por hoy. Resumamos y revisemos juntos, en qué necesitas concentrarte.

En general, el modo visitante es difícil de entender, los escenarios de aplicación son limitados y no es particularmente necesario, no recomiendo usarlo en el proyecto. Por lo tanto, para el ejemplo de procesamiento de archivos de recursos de la última lección, recomiendo usar el patrón de fábrica para el diseño y la implementación.

Además, hoy nos enfocamos en Double Dispatch. En un lenguaje de programación orientado a objetos, una llamada de método puede entenderse como un paso de mensaje (Dispatch). Cuando un objeto llama a un método de otro objeto, es equivalente a enviarle un mensaje, este mensaje debe incluir al menos el nombre del objeto, el nombre del método y los parámetros del método.

El llamado Single Dispatch se refiere al método con el que se ejecuta el objeto, que se determina según el tipo de tiempo de ejecución del objeto; qué método del objeto se ejecuta, que se determina según el tipo de tiempo de compilación del parámetro del método . El llamado envío doble se refiere al método con el que se ejecuta el objeto, que se determina según el tipo de tiempo de ejecución del objeto; qué método del objeto a ejecutar se determina según el tipo de tiempo de ejecución del parámetro del método.

Específicos del mecanismo gramatical de los lenguajes de programación, Single Dispatch y Double Dispatch están directamente relacionados con el polimorfismo y la sobrecarga de funciones. Los principales lenguajes de programación orientados a objetos actuales (por ejemplo, Java, C ++, C #) solo admiten Single Dispatch, no Double Dispatch.

discusión en clase

  1. ¿El patrón de visitante separa las operaciones de los objetos, viola los principios de diseño orientado a objetos? ¿Qué opinas sobre este problema?
  2. En el ejemplo de código que explica Single Dispatch, si cambiamos el código de SingleDispatchClass a lo siguiente y mantenemos los otros códigos sin cambios, ¿cuál será el resultado de DemoMain? ¿Por qué es este el resultado?
public class SingleDispatchClass {
  public void polymorphismFunction(ParentClass p) {
    p.f();
  }

  public void overloadFunction(ParentClass p) {
    p.f();
  }

  public void overloadFunction(ChildClass c) {
    c.f();
  }
}

Bienvenido a dejar un mensaje y compartir sus pensamientos conmigo. Si obtienes algo, puedes compartir este artículo con tus amigos.

Supongo que te gusta

Origin blog.csdn.net/fegus/article/details/130519357
Recomendado
Clasificación