style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-5164839828746352"
data-ad-slot="7563230308">

Refactorización: mejorando el diseño del código existente

Así se llamaría este libro http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201... si tuviera traducción al español.

A mi me cambió la forma en la que pensaba en código y me parece uno de esos libros imprescindibles para todo programador, si pueden comprarlo les recomiendo la versión de pasta dura.

La refactorización consiste en tomar una pieza de código y modificarla ( re - factorizarla ) de tal forma que haga exactamente lo mismo, pero su diseño mejore. Es también pieza fundamental en practicas como el TDD y es tan relevante como las pruebas mismas.

La parte de "que haga exactamente lo mismo" es F-U-N-D-A-M-E-N-T-A-L. No se trata de corregir algo que no funciona ( mucho menos de descomponer algo que si funciona ) si no de cambiar los atributos cualitativos de lo existente. Como esto de cualitativo es totalmente subjetivo ( lo que para mí es bueno para otra persona puede no serlo ) el criterio a tomar es responder a la pregunta ¿para que se usa ese código?

Para no escribir tanto mejor un ejemplo:

Extraer un método ( Extract method ).

Convertir un fragmento de código en un método cuyo nombre que explique el propósito del mismo

Por ejemplo se quiere imprimir una factura

// antes:
void imprimeFactura() {
    imprimeEncabezado();

    //imprime los detalles
    System.out.println ("Nombre:        " +  nombre );
    System.out.println ("Cantidad       " + getCantidad());
}

Después de la refactorización:

 // después
void imprimeFactura() {
    imprimeEncabezado();
    imprimeDetalles(getCantidad());
}

void imprimeDetalles (double cantidad) {
    System.out.println ("Nombre:        " +  nombre );
    System.out.println ("Cantidad       " + cantidad );  // corregido
}

El código hace exactamente lo mismo, pero ahora es más fácil leer.. Al hacer esto ese método puede ser reutilizado por otra parte del sistema ( por ejemplo imprimir nota ).

También se puede hacer exactamente lo contrario, quitár un método y ponerlo en línea:

Inline Method:
Poner el cuerpo de un método en el cuerpo de quién lo invoca y remover el método.

// antes:
int obtenerCalificacion() {
    return (masDeCincoRetrasos()) ? 2 : 1;
}
boolean masDeCincoRetrasos() {
    return numeroDeRetrasos > 5;
}
// después
int obtenerCalificacion() {
    return numeroDeRetrasos > 5 ? 2 : 1;
}

Es importante eliminar efectivamente el otro método, porque ya no se va a usar.

Las razones por las cuales aplicar uno ( extract method ) u otro ( inline method ) dependen totalmente de el objetivo de la aplicación, por lo que, para saber cuando hacer o no un refactoring es muy importante conocer que se intenta hacer con el software. La idea detrás de esto es que el diseño mejore. No tiene caso ir ciegamente un día quitando métodos y al siguiente ponerlos si no hay una razón para ello.

Otro aspecto fundamental es no romper la aplicación existente, aunque suene absurdo, pero pasa . Debe de haber forma de probar la aplicación para que después de hacer el refactoring se vea si sigue funcionando igual o no. De preferencia estas pruebas deben de poder correrse de forma automática ( es decir, que no dependa de un humano al que se le puede olvidar probar una parte ). Si no es posible garantizar que el software sigue corriendo igual entonces es mejor no hacer el refactor.

El tema es mucho muy amplio. El libro tiene una sección donde se explica con más detalle las razones para refactorizar, criterios para detectar areas de oportunidad ( descrito como "olores en el código" - code smells - ) y varios otras cosas. Vale muchísimo la pena.

Así que la siguiente vez que vean ese menú en su IDE, ya saben para que es:

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de CybJer

Muy importante la

Muy importante la refactorizacion pero me confunde que el metodo imprimeDetalles reciba la cantidad pero en el cuerpo la mande a traer del get, o solo es un error de dedo.

Aplica para cosas como esta (Me lo he encontrado mucho):

if(lista.size()==0){
//dicen que esta vacia
}else{
//dicen que no esta vacia
}
//En lugar de solo usar el isEmpty()
Imagen de rodrigo salado anaya

ammm

lista.size()==0 es lo mismo isEmpty()
Y
isEmpty() es diferente de if(true){...}else{...}
: )

Como que el ejemplo no me quedo muy claro.
Les dejo está liga va :) http://www.makinggoodsoftware.com/2011/03/27/the-obsession-with-beautifu...
Saludos.

Imagen de rodrigo salado anaya

re facto.... que??? jejeje

¿El código cuando se compila queda igualito si todo lo dejo en un método a que si lo re-factorizo?
¿Utilizar esta practica ayudaba el desempeño de la JVM, rendimiento o lo que sea?

Esta dirigido solo al ojo (o sentidos) humano. Por ejemplo si tengo 20 lineas en un método y lo re-factorizo, ayuda solo al humano o también a la máquina?

@Cybjer Fue estupidez mía,

@Cybjer Fue estupidez mía, error de copy/paste. Ya lo corregí.

@Rodrigo excelente pregunta, no es para un mejor desempeño ni como un mejor valor visual. Es para mejorar el diseño de la aplicación.

Tradicionalmente el diseño se hace antes de la codificación. Con el refactoring se puede cambiar el diseño después de la codificación dependiendo de lo que la aplicación demande.

Por ejemplo se podría dar el caso de que una clase necesitara una conexión a base de datos y tenga el método:

class SomeClass {
     public Connection getConnection(){...}

    public void someBusinessMethodThatUsesGetConnection() {
         Connection c = getConnection();
        // do something with c
    }
}

Imaginemos ahora que debido a los cambio en la aplicación ( nuevos requerimientos ) se necesita que otras dos clases hagan cosas similares a esa base de datos. Lo que se puede ocupar entonces es usar "Extraer clase" que es similar a extract method pero en este caso se crea una nueva clase:

class SomeClass {
     // movido a DbConnection
     //public Connection getConnection(){...}
     DbConnection db;
    public void someBusinessMethodThatUsesGetConnection() {
         Connection c = db.getConnection();
        // do something with c
    }
}
// Nueva clase después de extract method
class DbConnection {
    public Connection getConnection() { ... }
}

En este momento la funcionalidad de la aplicación sigue siendo la misma pero el diseño cambió. Incluso puede ser que el desempeño haya bajado tantito.

La gran ventaja que se tiene ahora, es que es posible reusar esa clase DbConnection en la clase con la nueva funcionalidad:

class SomeNewClass {
     DbConnection db; // Dadaaa
    public void aDifferentMethod () {
         Connection c = db.getConnection();
        // do something with c
    }
}

Esto parecería obvio, pero a muchas veces no lo es y la cantidad de codigo copy/paste'eado que hay por ahí es enorme.

La otra ventaja es que si la aplicación no contempla un escenario donde se use una clase extra para la conexión, pues no se crea en primer lugar. Dependiendo de la aplicación, un método para obtener una conexión a la bd puede ser suficiente.

¿Quedó más claro el ejemplo?

Imagen de beto.bateria

El refactoring esta muy

El refactoring esta muy relacionado con la arquitectura de software y entre otras cosas, busca:
- Que el codigo existente sea entendible para los programadores.
- El codigo se pueda reutilizar.
- En ocasiones mejorar el desempeño de la aplicacion.

Generalmente cuando vas a hacer una aplicacion, generas la arquitectura (podria ser en uml, en un pizarron, etc). Al empezar a codificar te das cuenta que se puede mejorar lo que estas haciendo, entonces le aplicas refactoring, es decir: quitas procedimientos de una parte y los pones en otra, divides procedimientos, creas clases o de plano borras alguna (poniendo el comportamiento en otras), etc.

Tambien se pude dar el caso de que los requerimientos cambien y/o aumenten y el codigo no ayude mucho a que se hagan esos cambios, entonces vamos a tener que, quitar, poner, borrar, etc. etc.

Se oye muy sencillo, pero necesitas tener una mente muy orientada a la factorizacion, descomposicion y abstraccion.

Imagen de CybJer

Se obtiene el mismo resultado

Se obtiene el mismo resultado pero NO es lo mismo "No intentemos reinventar el hilo negro"
Es como pretender ordenar una lista alfabeticamente a "Mano" pudiendo usar el Collections.sort
Como bien mencionas tambien vuelve el codigo mas entendible al ojo humano y si java nos facilita esas funciones es mas simple y entendible usarlas que reimplementarlas.
El proposito de refactorizar es que que haga exactamente lo mismo como menciono @OscarRyz.
Y bueno respecto a el link que expusiste, creo que si no hace lo mismo (lo vuelve dificil de integrar) no se esta haciendo una buena refactorizacion.

Pd: No todo el codigo es candidato a refactorizacion

Imagen de CesarAlducin

Entonces

Me parece bastante interesante tu comentario @CybJer entonces cual sera el parametro para saber si un codigo es candidato a refactorizacion.

Yo estoy a cargo de un sistema hecho con Servlets y estoy en Proceso de aplicar Refactorizacion ya que tiene demasiadas consultas a la base de datos siendo que se puede realizar una sola consulta para efectuar la operacion que yo necesito.

@OscarRyz ya tengo el libro y me parece bastante interesante muy buena propuesta.

@Cesar: "...¿cual sera el

@Cesar: "...¿cual sera el parametro para saber si un codigo es candidato a refactorizacion?..."

Para mí el criterio es:

...El criterio a tomar es responder a la pregunta ¿para que se usa ese código?...

Si tu código YA sirve y no va a cambiar y jala bien, puede ser el peor código pero en mi opinión deberias de dejarlo como está y aprovechar ese tiempo para construir cosas que aún no funcionan ( corregir bugs, nuevos features etc. ) Pero si tu código VA a ser cambiado ( quizá para agregar una nueva consulta ) entonces es candidato a Refactoring aún cuando parezca que esta muuy limpio.

Ya sé que se lo preguntabas a CybJer, pero pos ahista mi respuesta :)

Hablando de refactorización...

Ami me salta una duda que quiero consultar...

resulta que hace poco tuve que hacer refactorizar (reescribir casi todo) una clase que ya existia, entonces habiala necesidad de enviar un correo electronico o varios correos electronicos. Lo que hice fue programar la logica para una lista de mensajes que se puedan enviar y despues cuando necesites mandar un solo mensaje pues haces una lista de un solo elemento y tan tan... He buscado en codigos fuentes si alguien habia hecho esto pero no encontre casos similares y podria pensar ¿Mi codigo necesitara refactorizarse?

 
 
 

El codigo es lo siguiente (no lo publico todo porque ya saben, se enteran los jefes que pones codigo de la empresa y pa que quieres). Por cierto la clase MailMessage es una clase que hice para encapsular los datos necesarios para mandar el mail: emisor, receptor(es), titulo y cuerpo

        public void send(MailMessage mailMessages) throws
                        NoSuchProviderException,
                        MessagingException,
                        IOException {
       
                send(new MailMessage[]{mailMessages});
        }
       
       
        public void send(MailMessage[] mailMessages) throws
                        NoSuchProviderException,
                        MessagingException,
                        IOException {
       
                // .... Aqui se abre un canal SMTP y se envian todos los mensajes sobre la conexion abierta
                // .... Esto es para no abrir y cerrar conexion a cada mensaj que se necesite enviar
        }

 
 
 

como verán, el método public void send(MailMessage[] mailMessages) es el que realmente carga con la logica mientras que public void send(MailMessage mailMessages) solo hace referencia al metodo antes menconado y envia un array de una sola posición, entonces no sé bien si sea la mejor practica para enviar uno solo o varios mensjaes de un jalon.

 
 
Ustedes como ven?

Imagen de CybJer

CesarAlducin Como bien comenta

Como bien comenta @beto.bateria y @OscarRyz depende del diseño de tu aplicacion.
En un sistema bien diseñado o con un diseño factible talves solo requiera poner nombres significativos a variables y metodos (Para facilitar su mantenimiento a futuro).
En general la refactorizacion se trata de mejorar la aplicacion (visual y/o funcionalmente) obviamente tratando de no afectar el rendimiento (costo-beneficio).

No soy experto en la materia pero lo que yo haria en tu caso seria comprender:
¿por que? se hizo de esa manera (Talves si es conveniente el diseño actual)
¿Que beneficios traeria? (muy probablemente rendimiento, facilitar mantenimiento,etc)
La magnitud de los cambios (a que le pegas si lo tocas)

Seguramente los compañeros puedan corregir o aportar otros puntos que haya pasado por alto

Imagen de CybJer

Se ve bien yo no lo

Se ve bien yo no lo tocaba.
Si no mal recuerdo, se puede especificar una lista de destinatarios (si no es personalizado) yo solo he usado javamail

ah pues justo... de hecho

ah pues justo... de hecho hice la clase MailMessage pensando en que no tengas que meterte hasta Java Mail... Yo cuando hago codigo pienso en dejar usable una capa que te posibilite hacer incluso absurdamente facil su manejo...

Ejemplo:

MailSender mailSender = new MailSender();
mailSender.send(
        new MailMessage(
                "java.daba.doo@javamexico.org", // receptor
                "Este es el titulo del mail",
                "Saludos, aqui es el cuerpo del mensaje"
        )
);

mailSender.send(
        new MailMessage(
                "java.daba.doo@javamexico.org", // receptor
                "Otro mail pa' enviar",
                "Saludos, aqui es el cuerpo del mensaje"
        )
);

Pero claro que dejo opciones para usar otras funcionalidades de Java mail... por ejmplo para enviar archivos adjuntos tengo este contructor

public MailMessage(
        String[] recipients,
        String tittle,
        String body,
        Collection <byte[]> attachments
){
// ...

Por cierto que esto lo habia empatado con esa estructura de crear Factory... pero creo que es todo un despapaye tremendo para algo tan pequeño como enviar un mail

Imagen de CybJer

Jajajaja como se nos ocurren

Jajajaja como se nos ocurren las mismas soluciones para esos problemas yo hize lo mismo para mi trabajo anterior se la anduve presumiendo a @Davleax.
Pero ahora que mencionas el factory facade suena bien para Spring inyectar el MailSender donde lo necesitas y solo hacer la instancia del MailMessage para enviarlo

Imagen de CesarAlducin

Entiendo

Estoy acuerdo en lo que comentas @OscarRyz lo que pasa es que tenia o tengo la idea erronea que todo codigo que no sea facil de entender ( Muchas Consultas que nos llevan al mismo camino cuando se puede hacer una sola, variables sin utilizar etc,) se tiene que volver a programar o en este caso aplicar refactorizacion.

respecto a lo que comenta @CybJer en mi caso la aplicacion que quiero refactorizar solo es para consulta y realmente no afecta el costo-beneficio porque el unico factor que afecta que tenga tantas consultas es el tiempo que tarda en acceder a la base de datos, aunque los clientes que la ocupan ya estan acostumbrados a eso.

Saludos

Imagen de CybJer

Bueno entonces si hay almenos

Bueno entonces si hay almenos un beneficio (tiempo y facilitaria su mantenimiento) tambien piensa a largo plazo, ahora tarda algunos segundos ¿a futuro sera igual?, ¿vale la pena pagar el costo?.
No necesitas esperar a que sea un verdadero problema

Imagen de ezamudio

java.daba.doo

Vas a decir "cómo chinga este wey con Spring!" pero... Spring tiene una interfaz MailMessage que tiene dos implementaciones, una simple (para mensajes de texto sin formato) y una con MIME que le puedes poner archivos anexos, formato, etc.

Aunado a eso tiene una interfaz MailSender que es solamente algo abstracto y tiene una sub-interface con su correspondiente implementación, JavaMailSenderImpl.

Por qué tanta abstracción? Bueno pues porque en el código que solamente recibe mensajes como parámetros o devuelve mensajes como valor de retorno, puedes usar las interfaces abstractas MailMessage. El código que genera los mensajes sí necesita obviamente crear las instancias concretas de la clase simple o con MIME. Y puedes tener una propiedad en un componente, que sea tipo MailSender solamente, y a la hora de configurar los componentes le inyectas ahí un JavaMailSenderImpl; lo chido de esto es que en un ambiente de pruebas unitarias puedes pegarle un JavaMailSenderImpl "standalone" que maneje sus propias sesiones de JavaMail con una cuenta de pruebas, y en producción puedes poner un JavaMailSenderImpl que se conecte al servicio de JavaMail de tu contenedor JEE, por medio de JNDI. Así tu código requiere cero cambios y todo se queda en la parte de configuración. Y sigues el principio DRY.

@CesarAlducin Si es fácil

@CesarAlducin Si es fácil cometer ese error, yo personalmente lo cometí, tenía que entregar una funcionalidad y me pasé una semana "arreglando" algo que no estaba descompuesto ( el clásico JSP que se conecta directamente a la base de datos ). Era una página que casi no se usaba y había estado muy estable por casi un año en producción. Cuando me preguntaron sobre mi otro avance pues no llevaba nada. Me fleté todo el viernes por la noche, sábado en la mañana y entregué a tiempo el lunes.

Cuando el código está en crecimiento ( se está agregando nueva funcionalidad ) se debe de refactorizar continuamente, incluso dedicarle un día de la semana para ello, es como limpiar la casa, si dejas que pase un mes es más difícil a que si lo haces cada tres días.

Pero andar cambiando código que no va evolucionar solo por la razón misma de refactorizar es elocubración mental

style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-5164839828746352"
data-ad-slot="7563230308">