¿Buscas nuestro logo?
Aquí te dejamos una copia, pero si necesitas más opciones o quieres conocer más, visita nuestra área de marca.
¿Buscas nuestro logo?
Aquí te dejamos una copia, pero si necesitas más opciones o quieres conocer más, visita nuestra área de marca.
dev
Daniel Peña Hace 6 días Cargando comentarios…
En el post anterior vimos cómo empaquetar y distribuir nuestras recetas que contenían pasos y ejecuciones de recetas públicas.
En muchos casos, esas recetas pueden resolver nuestros problemas, pero hay veces que necesitamos hacer cosas más ad-hoc, por ejemplo, renombrar variables, cambiar signaturas de métodos y refactors que nos acompañan en el día a día.
En esta segunda parte del post vamos a ver cómo realizar modificaciones de clases Java internamente.
Vamos a definir una nueva variable en una clase. Debemos usar el "Visitor" de ClassDeclaration, ya que es a nivel de clase donde vamos a meter la variable. Nuestra receta quedará así:
Vemos que para cosas complejas podemos hacer uso de JavaTemplate. Esta utilidad nos permite escribir bloques Java de manera más sencilla como si fueran templates. En este ejemplo, además, vamos a crear una variable de tipo random, con lo que vamos a necesitar añadir ese import. Compilamos nuestro recetario:
Y ejecutamos en el proyecto:
Como vemos, nos ha añadido la declaración de la variable a todas las clases del proyecto. En muchos casos queremos establecer una condición para la ejecución de nuestro "Visitor".
Las precondiciones son clases Java que filtran antes de la ejecución del "Visitor". Vamos a deshacer los cambios ejecutados anteriormente y vamos crear la variable solo si la clase se extiende de una determinada clase padre. Descartamos los cambios anteriores para poder ejecutar nuevamente:
Creamos una clase "Vehicle" solo con el propósito de que "Ship" extienda de ella. Nos quedará así:
Y modificamos nuestra clase "Ship" para que extienda de "Vehicle":
Creamos la precondition de manera genérica, es decir, en el constructor podemos indicar qué clase es la que esperamos que extienda para cumplirse la condición.
Modificamos nuestra receta cambiando el método getVisitor para que, si se da la condición de que se extiende de "Vehicle", devuelva la ejecución del "Visitor".
Vamos a compilar nuestro recetario:
Y lo ejecutamos sobre el proyecto:
Por último, comprobamos que solo se ha visto afectada la clase "Ship".
Vamos a crear un método usando una precondición si el método no existe en la clase. Es decir, si la clase ya lo tiene entonces no lo mete.
Preparamos el código del proyecto añadiendo un método llamado mayúsculas a la clase Vehicle.java
Preparamos una precondition que se pueda reutilizar:
Preguntaremos si la clase declara un método llamado mayúsculas y, si es así, metemos otro método que hará uso de él. La receta nos queda así:
Como podemos ver, hemos vuelto a hacer uso de las preconditions y también del JavaTemplate para la declaración del método. Compilamos y preparamos la ejecución:
Vemos que nos ha añadido el método “concatenateWithMe” en la clase "Vehicle", que es la que declara el método.
Vamos a buscar un método que, si existe, cambie la primera línea para que sea una ejecución personalizada. En este caso, un new de un objeto “Ship”. Por simplificar, vamos a usar la precondition PreconditionHasMethodWithName.
Si la clase tiene un método con nombre main, vamos a modificar el contenido añadiendo una sentencia a la ejecución que instancie un “Ship”.
Nuestra receta queda así:
AddStatementToMethod
Preparamos la ejecución y nos queda así:
En el diff vemos que ha encontrado el método main y ha incluido el new “Ship”.
Esta receta va a crear un constructor con 2 parámetros. Esos 2 parámetros tendremos que definirlos a nivel de clase para poder asignarlos al constructor, así que esta receta va a ser un poco más compleja.
Vamos a cambiar el constructor de “Ship” para que pida 2 argumentos. Si cambiamos eso, el new que estamos haciendo en la clase App.java#main va a fallar porque necesitará proporcionar esos argumentos.
Esta receta requiere de 2 ejecuciones (recetas). La primera receta AddConstructorParametrizedToClass añadirá los campos y creará el constructor. La segunda receta UpdateConstructorInvocation, buscará las invocaciones al constructor de “Ship” y las cambiará para añadirle los nuevos argumentos que le vamos a pasar como argumentos en la receta. Nos quedarán así:
Y la otra:
Compilamos el recetario:
Queremos que estas 2 recetas se ejecuten en una transacción completa, ya que si lanzamos la primera, no vamos a poder lanzar la segunda porque dejaremos el código en un estado inestable que no llega a compilar.
A partir de un fichero YAML, creamos la configuración para ejecutar las 2 recetas, como hemos hecho en otras ocasiones.
Nos quedará así:
recipe_change_constructor.yaml
Y la ejecución indicando el fichero de configuración será así:
Analizamos el diff y vemos que ha cambiado todo:
Hemos visto cómo podemos escribir recetas propias si las públicas que existen no nos valen o necesitamos hacer algo más específico. Espero que esta guía te haya resultado útil y, si tienes dudas, te leo en comentarios👇
Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.
Usamos cookies propias y de terceros con fines analíticos y de personalización. Las puedes activar, configurar o rechazar. Configurar o rechazar.
Cuéntanos qué te parece.