Red de conocimiento informático - Material del sitio web - Implementación del modo Visitante en modo combinado

Implementación del modo Visitante en modo combinado

Este artículo comienza con un ejemplo dado que implementa el patrón compuesto para explicar cómo implementar código de lógica de negocios en esta estructura de datos. También presenta la forma no orientada a objetos de agregar métodos a la estructura compuesta. usando el acceso El modo de visitante (Visitor) y el modo de visitante mejorado (Visitor) se utilizan para implementar el mismo código de lógica de negocios, y se brindan las ventajas y desventajas de cada implementación respectivamente.

Lectores que tienen programas Java. desarrollo y diseño Desarrolladores con experiencia en patrones

A través de este artículo, los lectores pueden aprender cómo implementar varios métodos comerciales y sus ventajas y desventajas en el patrón compuesto

Patrón compuesto

El patrón de composición es un tipo de patrón estructural. El libro "Patrones de diseño" de GOF describe la intención de utilizar el patrón de composición de la siguiente manera: combinar objetos en una estructura de árbol para representar la estructura jerárquica de la parte y el todo. para el uso consistente de objetos individuales y objetos compuestos

El modo compuesto se usa ampliamente Según la definición del modo compuesto en GOF, el modo compuesto generalmente consta de la interfaz Componente. Clase Leaf y clase Composite. Ahora es necesario modelar la entidad de un sistema de gestión de productos de software. Una empresa ha desarrollado una serie de conjuntos de software (SofareSet), que incluyen productos de software de múltiples marcas (Marca). proporciona Lotus WebsPhere y otras marcas. Cada marca tiene una variedad de productos de software. Por ejemplo, Lotus de IBM tiene productos de servidor/cliente Domino. El diagrama de clases modelado es el siguiente (el código se puede encontrar en el archivo adjunto). artículo y todos los archivos fuente bajo el paquete de entidad de prueba)

 /p>

Como se muestra en la figura

( ) la interfaz SofareComponent corresponde a la interfaz Component en el modo compuesto Define el comportamiento predeterminado de todas las clases con interfaces

() La clase AbsSofareComposite corresponde a la clase Composite y es una clase abstracta. Todas las clases que pueden contener subnodos extienden esta clase. esta clase es para almacenar subcomponentes. El código reutilizable que implementa los métodos en la interfaz está escrito en esta clase

( ) La clase SofareSet hereda de la clase AbsSofareComposite y corresponde al conjunto de software. el conjunto de software puede contener directamente marcas (Brand) o contener directamente productos que no pertenecen a ninguna marca (Product)

() La clase Brand hereda de la clase AbsSofareComposite, que corresponde a la marca y contiene la marca atributo de nombre y se utiliza para almacenar instancias de la clase Producto

() La clase Producto es la clase Hoja correspondiente, que representa el nodo hoja. El nodo hoja no tiene nodos secundarios

. p>

Utilice diferentes métodos para implementar la lógica empresarial

Una vez establecida la estructura de datos, debe agregar métodos a la estructura de datos para implementar la lógica empresarial. Por ejemplo, en el ejemplo actual, existe. Tal requisito, dadas algunas selecciones de usuarios, un buen producto necesita calcular el precio total de este software seleccionado. Comencemos con cómo utilizar varios métodos para implementar esta lógica de negocios

Método de programación no orientada a objetos<. /p>

Este tipo de La idea de programación más simple es atravesar todos los nodos en la instancia de SofareSet. Si el objeto actual atravesado es Producto, entonces súmelos, continúe atravesando el siguiente nivel hasta que se completen todos los recorridos. El fragmento de código es el siguiente /** ?*? Obtener todos los nodos bajo un objeto SofareComponent.

¿El precio del conducto?*?@param?brand ?*?@return ?*/ public ?getTotalPrice(SofareComponent?sofareComponent)?{ SofareComponent?temp?=?sofareComponent; en La instancia es de tipo SofareSet if ?(temp?stanceof ?SofareSet)?{ Iterator?it?=?((SofareSet)?sofareComponent) getChilds() amp;erator() mientras ?(it hasNext())?{ //Recorrer temp?=?(SofareComponent)?it next(); //Si el subobjeto es una acumulación directa del tipo de Producto if ?(temp?stanceof ?Product)?{ Product?product?=?(Product) ?temp; totalPrice ? =?product getPrice(); }? else ?(temp? exampleof ?Brand)?{? // Si el subobjeto es de tipo Marca, recorre todos los productos bajo Marca y acumula Marca? ?=?(Marca )?temp; precio total? =?getBrandPrice(marca); } } }? else if ?(temp? instanciade ?Marca)?{ //Si la instancia entrante es del tipo SofareSet, itera por todos productos bajo Marca y acumularlos totalPrice? =?getBrandPrice((Brand)?temp }? else if ?(temp? exampleof ?Product)?{ //Si el subobjeto es de tipo Producto, devuelve el precio directamente return ?((Product)?temp) getPrice (); } return ?totalPrice } /** ?*?Obtener los precios de todos los productos bajo un determinado objeto de marca?*?@param?brand ?*?@return ?* / privado ?double ?getBrandPrice(Marca?marca) ?{ Iterator?brandIt?=?brand getChilds(erator(); double ?totalPrice?=? ; while ?(brandIt hasNext())?{ Producto?producto?=?( Product)?brandIt next(); totalPrice? = ?product getPrice(); return ?totalPrice }

La ventaja de este código es que al implementar la lógica empresarial, no es necesario realizar cambios. La estructura de datos determinada previamente es más eficiente. La desventaja es que el código es desordenado y frecuente. El código que usa instancia de para determinar el tipo y la conversión de tipo forzada no es muy legible. Si hay más capas, el código será más. confuso

Método de programación orientada a objetos (el método de calcular el precio

Agregar a la estructura de datos)

A continuación podemos hacer esto de forma orientada a objetos. Agregar un método llamado getTotalPrice a la interfaz SoftWareComponent. El método se declara de la siguiente manera /** ?*? objetos de nodo en el nodo La suma de precios ?*?@return ?*/ public ?getTotalPrice(); Dado que ambas clases Brand y SofareSet heredan AbsSofareComposite, solo necesitamos implementar el método getTotalPrice en la clase AbsSofareComposite de la siguiente manera public ? double ?getTotalPrice( )?{ Iterator?it?=?erator(); double ?price?=? ; (tieneNext())?{ SofareComponent?sofareComponent?=?(SofareComponent)?it siguiente(); /¿Llamada recursiva automática al método GetTotalPrice de cada objeto y acumular precio? =?sofareComponent getTotalPrice(); } return ?price } Implementar lo siguiente en la clase de Producto public ?getTotalPrice(){ return ?price } el total de un objeto afuera Simplemente escriba el precio de esta manera (este código se puede encontrar en el ejemplo de prueba empresarial SofareManager en este artículo) //El método getMockData() devuelve datos SofareComponent?data?=?getMockData() //Solo; llame al objeto de datos directamente El método getTotalPrice? puede devolver el precio de todos los objetos de producto bajo el objeto double ?price?=?data ?getTotalPrice() // Después de encontrar un objeto, llame a su método getTotalPrice directamente para devolver el precio total. ?=?data ?findSofareComponentByID (id) getTotalPrice();

Ahora la implementación de la lógica empresarial se coloca en la estructura de datos (en la estructura del modo combinado. La ventaja es obvia). gestiona la implementación y la implementación de su propio código comercial relacionado. En comparación con la implementación orientada a procesos mencionada anteriormente, no hay instancia y conversión de tipo forzada, pero la desventaja es que si necesita agregar un nuevo método comercial, será muy difícil. problemático. Primero debe declarar el método en la interfaz SoftWareComponent y luego declararlo en cada subclase Implementar y recompilar

Usar el modo visitante

Usar el modo visitante puede resolver los problemas mencionados. Lo anterior si desea agregar o eliminar métodos de funciones comerciales con frecuencia, debe volver a implementar y compilar el programa con frecuencia de acuerdo con el principio SRP (principio de responsabilidad única), uno de los principios del diseño orientado a objetos. Más de una responsabilidad, habrá múltiples razones para el cambio de clase, lo que provocará cambios de diseño frágiles. El diseño original puede sufrir daños inesperados. A continuación presentamos una interfaz llamada Visitante. La interfaz define los métodos de acceso para cada subclase. siguiente: ¿interfaz pública? ¿Visitante?{ ¿público nulo? VisitaMarca( Br.

y?brand); public ? void ?visitSofareSet(SofareSet?sofareSet); public ?visitProduct(Product?product } El método visitBrand se utiliza al acceder al nodo del objeto Marca. SofareComponent Agregue un método public? void?accept(Visitor?visitor); implemente el método de aceptación en la interfaz en SofareSet. Primero, llame directamente al método visitSofareSet en la interfaz Visitor. El parámetro pasado es su propio objeto. aceptar método público? vacío del subobjeto. ?accept(Visitor?visitor)?{ visitante visitSofareSet( this ); =?(SofareComponent)it next(); componente Accept(visitor); } } ? Para implementar el método de aceptación en la interfaz en Brand, primero llame directamente al método visitBrand en la interfaz Visitor. luego llame recursivamente al método de aceptación del subobjeto public ?accept(Visitor?visitor )?{ visitante visitBrand( this ); ?ponent?=?(SofareComponent)it next(); ponent aceptar(visitor); } } ? un método en la clase principal AbsSofareComposite y luego llame directamente a este método en la clase principal. Aquí, para facilitar la explicación, están escritos en dos subclases respectivamente. Implemente el método de aceptación en la interfaz en Producto y llame directamente al método visitProduct del Visitante. interfaz. Public ? void ?accept(Visitor?visitor)?{ visitante visitProduct( this } ? Lo siguiente debe implementar Visitor El nombre de la clase de interfaz es CaculateTotalPriceVisitor. El código de implementación de la lógica empresarial para calcular el precio total es el siguiente: public. ? clase ?CaculateTotalPriceVisitor? implementa ?Visitor?{ privado ?totalPrice; público ?visitBrand(Marca?marca)?{ } público ?visitSofareSet(SofareSet?sofareSet)?{ } público void? product)?{ //Cada vez que se encuentra Pro en la estructura combinada

Este método se llamará cuando el nodo del objeto del conducto sea PrecioTotal? =?product getPrice(); } public ?getTotalPrice()?{ return ?totalPrice; clase Dado que ni Brand ni SofareSet tienen un precio, en la implementación, solo necesita acumular totalPrice en el método visitProduct. Si necesita calcular el precio total, escríbalo así afuera (este código se puede encontrar en el ejemplo de prueba empresarial). SofareManager en este artículo) //Establecer un nuevo objeto visitante CaculateTotalPriceVisitor?visitor?=? new ?CaculateTotalPriceVisitor() //Pasar el objeto visitante a los datos de la estructura aceptar(visitor); //Llamar al método getTotalPrice() del visitante. el objeto devuelve el precio total double?price?=?visitor getTotalPrice();

El siguiente es su diagrama de secuencia. En el método principal de la clase SofareManager, llame al método de aceptación del objeto del conjunto de software (datos. ) y le pasa el objeto visitante generado. aceptar El método comienza a llamar recursivamente al método de aceptación de cada subobjeto. Si el objeto actual es una instancia de SofareSet, se llama al método visitSofareSet del objeto visitante. los datos del nodo y luego regresa y así sucesivamente, atravesando el objeto Marca y el objeto Producto. Este también es el caso. De manera similar a la lógica actual, se calcula el precio total del producto de software. atravesado, el precio del producto se saca y se acumula. Finalmente, una vez completado el recorrido de la estructura, se llama al método getTotalPrice del objeto visitante para devolver el precio total de los (datos) del objeto del conjunto de software dado. Si necesita agregar una nueva lógica de cálculo al precio, puede implementar diferentes métodos lógicos implementando solo la interfaz Visitor y pasando la instancia de esta clase al método de aceptación del objeto de datos

 

.

Haga clic en el pequeño Ver la imagen grande

Podemos ver que el modo visitante es una buena solución para agregar nuevo código comercial sin volver a compilar el código existente. Sin embargo, este modo no está exento. Deficiencias si está en el modo combinado. Si se agrega una nueva subclase a la estructura, la interfaz de Visitante también cambiará, lo que hará que todas las subclases de Visitante necesiten implementar nuevos métodos. Por lo tanto, este patrón de visitante es adecuado para situaciones en las que la estructura. no cambia con frecuencia

Acceso mejorado Modo visitante

Anteriormente hablamos sobre cómo usar el modo Visitante y las ventajas y desventajas de usar este modo. Pongamos un ejemplo específico para ilustrar. Supongamos ahora que el cliente propone el concepto de un conjunto de productos (ProductSet). A medida que cambia la versión del software de la empresa, para aumentar, es necesario colocar la misma versión de productos en un conjunto de productos (ProductSet), y una marca contiene varios conjuntos de productos. Dado que ahora se agrega un nodo a la estructura combinada, un nodo llamado método visitProductSet hará que todas las clases en el sistema original que han implementado la interfaz Visitor deban volver a implementarse y compilarse. Este problema se puede resolver utilizando el mecanismo de reflexión de Java.

Utilice el mecanismo de reflexión de métodos de Java para implementar el modo visitante

Primero necesitamos cambiar el nombre de la interfaz Visitor a ReflectionVisitor como se muestra a continuación public ?reflectionVisitor?{ /** ?*?Define un método para acceder al nodo?*?@param?sofareComposite ?*/ public ?void ?visitSofareComposite ( Object ?sofareComposite); }

Cualquier objeto puede ser aceptado en el método de interfaz actual (el parámetro es Object)

Lo siguiente implementa la interfaz ReflectionVisitor llamada ReflectionVisitorImpl. sigue la clase pública? ReflectionVisitorImpl? implementa? ReflectionVisitor? {public? ?visit?node ?should?not?be?null ); } //Ensamblar la matriz de clases, es decir, el tipo de parámetro al llamar al método dinámico Class []?classes?=? {?sofareComposite getClass()?}; //Ensamblar el valor correspondiente a la matriz de clases Object []?objects?=? new ? Object []?{?sofareComposite?}; m?=?getClass() getMethod( visita ? clases); //Llame al método m invoke( this ?objects catch }?( NoSuchMethodException ?e)?{ //No se encontró el método correspondiente println( ¿No implementaste el método de visita para la clase? ?visit?method ); e printStackTrace() ; } } }

Este código primero determina si el objeto entrante es un puntero nulo, luego crea una matriz de clases y una matriz de objetos, y luego usa el método getMethod. para obtener el nombre del método, que es visitar. El parámetro del método es la clase correspondiente al objeto sofáComposite. Cuando el método finalmente llama a este método, puede ocurrir una excepción NoSuchMethodException. La aparición de esta excepción indica que es su subclase o la actual. la clase no tiene un método de visita correspondiente al parámetro pasado.

A continuación, escriba uno nuevo Versión V

La clase isitor extiende la clase ReflectionVisitorImpl recién escrita y se llama CaculateTotalPriceReflectionVisitor como se muestra a continuación public?class?CaculateTotalPriceReflectionVisitor? extends?ReflectionVisitorImpl?{ private?double?totalPrice? ; } public ? void ?visit(SofareSet?sofareSet)?{ System out println( No?price?for?sofare?set } public ?getTotalPrice()?{ return ?totalPrice ? declarado en (porque en la clase ReflectionVisitorImpl se encuentra un método llamado visita cuyo parámetro coincide con el objeto pasado. Uno es para Producto y el otro es para SofareSet. No hay precio en SofareSet, solo el objeto actual). es una instancia de la clase Producto, simplemente sume los precios. Si se agrega una nueva clase a la estructura del modo combinado, solo necesita declarar un método de visita en la clase de extensión de ReflectionVisitorImpl. El parámetro de este método es new. clase agregada Para el ejemplo en el texto Simplemente agregue el siguiente método public? void?visit(ProductSet?productSet)?{ //código implementado}

Cambie el parámetro del método de aceptación en la interfaz SofareComponent del combinado. estructura de la interfaz de visitante modificada como se muestra a continuación

public void aceptar(ReflectionVisitor visitante

Dado que los códigos para implementar el método de aceptación anterior en las clases SofareSet Brand y ProductSet son los mismos); , el código se abstrae a la capa superior *** Algunas clases abstractas AbsSofareComposite son las siguientes: public ? void ?accept(ReflectionVisitor?visitor)?{ visitante visitSofareComposite( this ); (it hasNext())?{ SofareComponent?ponent ?=?(SofareComponent)?it next(); // Llama recursivamente al método de aceptación del subobjeto Accept(visitor); afuera, el código es el siguiente (en el ejemplo de este artículo, prueba el negocio SofareManager, este código se puede encontrar en) //Crear un nuevo objeto Visitante CaculateTotalPriceReflectionVisitor?reflectionVisitor new ?CaculateTotalPrice?

ReflectionVisitor(); //Pasa el objeto visitante a los datos de la estructura Accept(reflectionVisitor); //Llamar al método getTotalPrice() del objeto visitante devuelve el precio total double ?price?=?reflectionVisitor getTotalPrice();

Además, dado que el método de visita de la clase Brand no está implementado, se generará una excepción NoSuchMethodException cuando la estructura combinada atraviese el nodo Brand, es decir, no hay implementación del método de nodo y se emitirá una oración. se imprimirá en el programa actual

No implementó el método de visita para la clase: clase entidad de prueba Marca

Si ocurren otras excepciones al ejecutar el programa, consulte la API de Java correspondiente documentación

Mejoras actuales En el último modo de visitante, si agrega o elimina nodos en la estructura combinada, no tendrá ningún impacto en el Visitante ya implementado. Si agrega un nuevo método comercial, solo necesita. para ampliar la clase ReflectionVisitorImpl, para que resuelva bien el problema de acceso Problemas con el modo visitante

Mejorando la integración de la implementación del modo visitante con el código existente

Hasta ahora, el modo visitante mejorado. Parece haber resuelto todos los problemas que han surgido, pero considerando la siguiente situación, ahora necesita escribir una biblioteca de etiquetas JSP (TagLib). Esta biblioteca de etiquetas también debe tener la función de Visitante (es decir, necesita la función). función de atravesar nodos). El contenido del nodo se puede imprimir en la página HTML según sea necesario. Debido a la etiqueta, debe heredar la clase correspondiente (como TagSupport). No será posible porque Java no permite la herencia múltiple. Sin embargo, podemos mejorar el código original de ReflectionVisitorImpl para resolver esta situación. La nueva clase de implementación Visitor se llama. El código de NewReflectionVisitorImpl es el siguiente: ¿la clase pública ?NewReflectionVisitorImpl? ///? ¿La clase que implementa el método de visita? private ? Object ?targetObject; //El constructor pasa la clase que implementa el método de visita public ?NewReflectionVisitorImpl( Object ?targetObject )?{ if ?(targetObject?==? null ) throw ? new ? NullPointerException ( The?target?object?should?not?be?null! ); este targetObject?=?targetObject } public ?visitSofareComposite( Object ?sofareComposite)?{ //...Igual que el ejemplo anterior intente ?{ //Encuentre el método de visita del objeto de destino Método?m?=?targetObject getClass() getMethod( visite ?clases //Llame al métodomi);

nvoke(targetObject ?objects); }? catch ?( NoSuchMethodException ?e)?{ //...Igual que el ejemplo anterior}? catch ?( Exception ?e)?{ //...Igual que el ejemplo anterior} } } ? La implementación de esta clase es casi la misma que la implementación anterior. Hay un constructor más en los parámetros del constructor, se pasa la clase que implementa el método de visita y se mantiene una referencia a la clase. lo más importante son las siguientes dos líneas de código. //Encontrar el método de visita Method?m?=?targetObject getClass() getMethod(visit?classes //Llamar al método m invoke(targetObject?objects); /p>

Originalmente en el código, el método de visita se encuentra en su propia clase y sus subclases, pero ahora el método de visita se encuentra en la clase objetivo mantenida

Ahora necesita escribir la etiqueta class. Esta clase extiende la clase TagSupport como se muestra a continuación (para facilitar la explicación, se proporciona una clase TagSupport simulada con los ejemplos de este artículo) public ?myTag extends ?TagSupport?{ SofareComponent?sofareComponent?=? ; private ? double ?totalPrice?=? ; public ?doEngTag() ?{ //Crea un objeto visitante y pasa al objeto visitante ReflectionVisitor?=? SofareComponent aceptar(visitante); //Imprimir el precio println(preciototal return } //Implementa el método de visita para Producto publico ?visita(Producto?producto)?{ preciototal? ; } public ? void ?visit(Brand?brand)?{ out println( brand getId()? ?brand getDescription() } // Para otros códigos, consulte el programa fuente adjunto a este artículo... } Si desea probar el código escrito anteriormente (se puede encontrar en el ejemplo de prueba empresarial SofareManager en este artículo Este código)) es el siguiente: //el método getMockData() devuelve datos SofareComponent?data?=?getMockData(); myTag?=? new ?MyTag(); myTag setSofareComponent(data); //Calcula el precio total e imprime myTag doEngTag(); Puedes ver que el problema de la herencia múltiple está bien resuelto. Mecanismo de reflexión de Java, para que el patrón de visitante se pueda aplicar mejor a su aplicación. También puede ver Las clases donde se encuentra el método de visita ya no implementan la interfaz ReflectionVisi.

Se puede decir que Tor es una implementación especial del modo visitante con soporte para el lenguaje Java.

Si le preocupan los problemas de eficiencia causados ​​por la introducción del mecanismo de reflexión de clases, puede almacenar en búfer el método. objeto de alguna manera para que no lo haga. Encontrar el método de visita del objeto entrante cada vez puede mejorar parcialmente la eficiencia

Conclusión lishixinzhi/Article/program/Java/gj/201311/11152