Refactorización en Agile

La refactorización es la práctica de mejorar continuamente el diseño del código existente, sin cambiar el comportamiento fundamental. En Agile, los equipos mantienen y mejoran su código de manera incremental de Sprint a Sprint. Si el código no se refactoriza en un proyecto Agile, dará como resultado una calidad de código deficiente, como dependencias no saludables entre clases o paquetes, asignación incorrecta de responsabilidades de clase, demasiadas responsabilidades por método o clase, código duplicado y una variedad de otros tipos de problemas. confusión y desorden. La refactorización ayuda a eliminar este caos y simplifica el código complejo y poco claro. Las siguientes son las definiciones citadas por varios expertos en Agile sobre conceptos de refactorización:  

Una refactorización es una «transformación que preserva el comportamiento» – Joshua Kerievsky  

La refactorización es “un cambio realizado en la estructura interna del software para que sea más fácil de entender y más económico de modificar sin cambiar su comportamiento observable” – Martin Fowler  

Es mejor refactorizar de forma continua, en lugar de por fases. La refactorización continua evita que el código se complique y ayuda a mantener el código limpio y fácil de mantener. En el desarrollo Agile, puede haber sprints cortos y separados para adaptarse a la refactorización. Este módulo proporciona una descripción general de la práctica técnica/de ingeniería de refactorización que se implementaría en proyectos ágiles.

Desafíos:

Aunque la refactorización aporta muchos beneficios a la calidad del código del software, existen múltiples desafíos que disuaden a los desarrolladores de proyectos Agile de refactorizar continuamente el código. Los siguientes son algunos desafíos que se ven principalmente en proyectos ágiles  

  • Restricción de tiempo: el tiempo es el mayor desafío para hacer la refactorización en proyectos Agile, ya que los Sprints tienen un límite de tiempo con un conjunto definido de entregables.
  • Renuencia : si el código funciona bien sin que se haya realizado ninguna refactorización, habrá una inclinación a no revisar el código. Esto se debe principalmente a la mentalidad de que no hay errores y, por lo tanto, no es necesario realizar actividades adicionales, como la refactorización.
  • Integración con sucursales : integrar el código en diferentes sucursales después de la refactorización se considera un desafío
  • Factor de miedo : el desarrollador a menudo teme que la refactorización introduzca errores y rompa la funcionalidad existente que funciona bien.
  • Nueva prueba : en caso de que no haya suites de prueba automatizadas disponibles, se desaconseja al desarrollador que realice una refactorización con el esfuerzo adicional de la prueba manual para verificar la funcionalidad.
  • Compatibilidad con versiones anteriores: la compatibilidad con versiones anteriores a menudo impide que los desarrolladores comiencen los esfuerzos de refactorización.

Motivación para la refactorización:

Los siguientes puntos de motivación son más comunes entre los desarrolladores mientras realizan la refactorización  

  • Se vuelve más fácil agregar código nuevo
  • Se mejora el diseño del código existente.
  • Ayuda a obtener una mejor comprensión del código.
  • Hace que la codificación sea menos molesta

Ventajas de la refactorización:

La refactorización en pequeños pasos ayuda a evitar que se introduzcan defectos. Los siguientes puntos pueden percibirse mejor a partir de los beneficios de implementar la Refactorización.  

  • Mejora la extensibilidad del software
  • Reduzca los gastos de mantenimiento del código.
  • Proporciona código estandarizado
  • Mejora de la arquitectura sin afectar el comportamiento del software
  • Proporciona código más legible y modular.
  • Componente modular refactorizado para maximizar la posible reutilización

Directrices de diseño para proyectos ágiles:  

En un proyecto de desarrollo de software tradicional, los requisitos y los planes se establecen antes de que comience el desarrollo, lo que permite al equipo conocer el camino a seguir con una confianza razonable de que los requisitos o el diseño no cambiarán drásticamente en el medio, mientras que el método Agile es permitir y aceptar el cambio. En un proyecto Agile, los requisitos pueden cambiar en cualquier punto del ciclo del proyecto. Por lo tanto, es imperativo que los equipos ágiles tengan un código en una posición en la que puedan aceptar convenientemente un nuevo requisito o cambio.  

El buen diseño es un principio importante de los proyectos del manifiesto Agile: “La atención continua a la excelencia técnica y el buen diseño mejoran la agilidad”.

Precauciones para la refactorización:

  • Demasiado enfoque en el patrón quita el enfoque de escribir código pequeño, simple y comprensible.
  • Mire los patrones desde una perspectiva de refactorización y no solo como elementos reutilizables.

¿Cómo verificar el código de la facultad?

‘Code Smell’ es la frase acuñada por Kent Back y Martin Fowler. Algunos de los “olores en el código” más importantes son:

S. No. Oler Descripción
1. Código duplicado Existe un código idéntico o muy similar en más de una ubicación
2. Método largo Un método/función/procedimiento que ha crecido demasiado
3. clase larga Una clase que ha crecido demasiado
4. Demasiados parámetros Una larga lista de parámetros es difícil de leer y complica llamar y probar la función.
5. Envidia característica Una clase que usa métodos de otra clase en exceso.
6. Clase perezosa Una clase que hace muy poco
7. Complejidad artificial Uso forzado de patrones de diseño demasiado complicados donde el diseño más simple sería suficiente.
8. Complejo para depurar El código se ha vuelto lo suficientemente complejo como para depurar

Directrices para la refactorización:

  • Asegúrese de que el código funcione antes de comenzar.
  • Asegúrese de que un conjunto de pruebas automatizado esté disponible y proporcione una buena cobertura.
  • Ejecute las pruebas con frecuencia antes, durante y después de cada refactorización.
  • Antes de cada refactorización, utilice una herramienta de control de versiones y guarde un punto de control. Esto no solo significa que la recuperación se puede hacer rápidamente de los desastres, sino que también significa que la refactorización se puede probar y luego retroceder si no está satisfecho con el código refactorizado.
  • Divida cada refactorización en unidades más pequeñas.
  • Finalmente, si hay una herramienta de refactorización disponible en su entorno, utilícela.

Implementación de Técnicas de Refactorización:  

Existen múltiples técnicas de refactorización disponibles que mejorarán el código existente en términos de rendimiento y mantenibilidad. El propósito básico de la refactorización en Agile o cualquier otra metodología es “Dejar un módulo en mejor estado que como lo encontraste”  

Martin Fowler clasifica las técnicas de refactorización de la siguiente manera:  

  • Métodos de composición
  • Mover funciones dentro de los objetos
  • Organización de datos
  • Simplificar la expresión condicional
  • Simplificar las llamadas a métodos

Cada categoría tiene ciertas técnicas de refactorización. Algunas técnicas se explican a continuación.  

1. Métodos de composición: se ocupa del empaquetado adecuado del código de método o función. Los métodos/funciones grandes que tienen una implementación lógica compleja muy larga pueden reducir la capacidad de mantenimiento y la legibilidad del código. Aplicar técnicas de refactorización como métodos de extracción ayudará a que el código sea más mantenible y legible. A continuación se detallan algunas de las técnicas de refactorización en la composición de métodos/funciones:  

  • Método de extracción
  • Método en línea
  • Reemplazar método con objeto de método

Método de extracción: esta es una de las técnicas de refactorización más utilizadas. La implementación detrás de esta técnica es muy simple. Consiste en dividir métodos largos cambiando fragmentos complejos de código en nuevos métodos que tienen identificadores muy descriptivos. Este método se utiliza cuando: 

  • El código consta de métodos largos que cubren múltiples flujos lógicos que se pueden dividir en métodos más pequeños.
  • Si el mismo código está duplicado en más de un flujo, muévalo a un solo método y llame desde todos los demás flujos

Ejemplo:

Before Refactoring: 
void printStudentRecord() {

   printSchoolName();

   // print details  
   System.out.println("Id: " + _id);

   System.out.println("name " + _name);

}
After Refactoring:
void printStudentRecord() {
   printSchoolName();
   printStudentDetails();
}
void printStudentDetails() {
   // print details  
   System.out.println("Id: " + _id);
   System.out.println("name " + _name);
}

Las ventajas de este método son:  

  • Reorganización adecuada del código al agrupar un conjunto de declaraciones en el único método más pequeño
  • Reduce la duplicación de código
  • Aumenta la legibilidad del código.

Método en línea: a diferencia de la técnica de refactorización del método de extracción, las técnicas del método en línea sugieren reemplazar una llamada de método/función con el cuerpo del método/función. Se sugiere cuando el código fuente del método/función es muy pequeño. Este método se utiliza cuando:  

  • Cuando una llamada de función provoca un cuello de botella en el rendimiento
  • Cuando el código en línea aumenta la legibilidad del código ya que el cuerpo de la función es el mismo que el nombre de la función
  • Cuando hay demasiadas delegaciones en el código

Ejemplo:

Before Refactoring 
int getRating() {

   return (moreThanFiveLateDeliveries()) ? 2 : 1;

}

boolean moreThanFiveLateDeliveries() {

   return _numberOfLateDeliveries > 5;

}

After Refactoring 
int getRating() {

   return (_numberOfLateDeliveries > 5) ? 2 : 1;

}

Las ventajas de este método son:  

  • Reducción de la complejidad innecesaria, reducción de la sobrecarga de llamadas a métodos
  • Aumenta la legibilidad del código.

Reemplace el método con el objeto del método: la regla general del buen código es que debe ser legible y fácilmente manejable. En ocasiones, podemos encontrarnos con un método muy largo que tiene una implementación lógica complicada realizada en su interior con muchas variables locales. Dicho método no se puede simplificar aplicando la técnica del método de extracción debido al uso de demasiadas variables locales, porque pasar tantas variables sería tan complicado como el método largo en sí. En tales casos, se puede crear una clase para tal método. Esta clase tendrá todas las variables locales de ese método largo como sus miembros de datos. Uno de los métodos será el método largo. La lógica complicada de este método se puede simplificar fácilmente aplicando la técnica de métodos de extracción.  

Ejemplo:

Before Refactoring
class Employee {

   private:

       // some declarations  
       double CalSalary(float fVCPI, float fCPI, float fPPF) {

           double dIncomeTax;

           double dHRA;

           double dLTA;

           // complicated salary calculations  

       }

};
After Refactoring 
class SalaryCalculator {
   private: float m_fVCPI;

   float m_fCPI;

   float m_fPPF;

   double m_dIncomeTax;

   double m_dHRA;

   double m_dLTA;

   public:

       SalaryCalculator(float fVCPIpercent, float fCPIpercent, float 
       fPF, double dIncomeTaxPercent, double dHRA, double dLTA) {

           // initialize private data members  
       }

   double CalculateSal() {

       // complicated salary calculations  
   } };

class Employee {

   private:

       // some declarations  
       public:

       double CalSalary() {

           SalaryCalculator obj(14, 13, 200.43, dITPercent, dHRA, dLTA);

           return obj.CalculateSal();

       }};

2. Funciones móviles dentro de los objetos:

Una de las partes más importantes y críticas del diseño de objetos es decidir dónde asignar las responsabilidades. Hay muchas opciones. Las técnicas de refactorización pueden ayudarnos a decidir lo mismo. Las siguientes son algunas de las técnicas de refactorización que se pueden utilizar para decidir la asignación de responsabilidad adecuada:  

  • Move Method: Considere un escenario para realizar una funcionalidad. Un método utiliza miembros (datos o funciones) de otra clase, es decir, Clase-2, varias veces, en comparación con aquella en la que está escrito, es decir, Clase-1. Luego, dicho código se puede refactorizar utilizando la técnica Move Method. De acuerdo con esta técnica, en tales casos, ese método se puede mover a Clase-2. Class-1 puede llamar a este método para completar la funcionalidad necesaria y el método puede eliminarse de Class-1 por completo. La técnica de método de movimiento también se puede aplicar cuando un método se usa un número máximo de veces por una de las clases en las que se declara y define.
Move Method Refactoring Technique

 

  • Mover campo: si un miembro de una clase se usa un número máximo de veces por métodos de acceso de alguna otra clase, dicho campo se puede mover a otra clase. Esto necesita modificaciones necesarias en la clase anterior que tenía ese miembro de datos. Las técnicas del método Move también se aplicarán cuando se identifiquen nuevas clases durante la fase de desarrollo.
Move Field Refactoring Technique

 

  • Ocultar delegado:Una de las características importantes del diseño orientado a objetos es que si se realizan cambios en una de las clases, entonces ninguna otra clase o muy pocas clases deberían tener un impacto en su código debido a este cambio. Si una clase envía sus requests a otra clase, dicha clase puede denominarse clase de cliente. La clase que atiende las requests enviadas puede denominarse clase de servidor. Considere una función que tiene que obtener un objeto de clase B llamando a uno de los métodos de clase A. Esta función también tiene que llamar a un método de clase B para obtener los datos requeridos. Esto significa que la función tiene que interactuar primero con la clase A y luego con la clase B. En este caso, la estructura interna de la clase B se revela parcialmente a esa función. Lo mismo se puede evitar si la clase A tiene un método que obtendrá las cosas necesarias de la clase B y las enviará a la función del cliente. Esta parte de delegación se puede ocultar de la función de cliente. El siguiente diagrama muestra una presentación pictórica donde la Clase ‘Departamento’ está oculta del ‘Cliente’, ya que la llamada a la Clase ‘Departamento’ se mueve al método de clase ‘Persona’.
Hide Delegate Refactoring Technique

 

3. Organización de datos: 

La siguiente sección ilustra algunas técnicas que mejorarían la organización de datos. Esto también facilita el trabajo con datos. Reemplazar array con objeto: una de las estructuras de datos más comunes utilizadas para la organización de datos es una array. La array es una colección de datos homogéneos en ubicaciones contiguas de memoria que tienen el mismo nombre. Aunque homogéneo, el mismo arreglo puede contener datos con un significado diferente causando confusión al acceder a esta estructura de datos. En tales casos, es bueno reemplazar la array con un objeto. 

4. Simplificación de expresiones condicionales: 

Comprender la lógica condicional implementada en un programa puede ser una de las tareas más desafiantes. Usando técnicas de refactorización, la lógica condicional se puede simplificar para que el código se pueda entender fácilmente. Esta sección tiene algunas técnicas de refactorización mediante las cuales la lógica condicional se puede simplificar fácilmente.  

5. Simplificar las llamadas a métodos: 

Los objetos pueden interactuar entre sí y con el mundo exterior a través de métodos públicos. Estos métodos deberían ser fáciles de entender, entonces todo el diseño orientado a objetos será fácil de entender. En esta sección, discutiremos algunas técnicas de refactorización que facilitarán las llamadas a métodos.  

Refactorización en desarrollo dirigido por pruebas:

Test-Driven Development (TDD) es un enfoque de ingeniería de software que consiste en escribir casos de prueba fallidos que cubran primero la funcionalidad. Y luego implementar el código necesario para pasar las pruebas y finalmente refactorizar el código sin cambiar el comportamiento externo.  

TDD se ocupa de 2 tipos de pruebas, es decir  

  • Pruebas unitarias: se utilizan para verificar la funcionalidad de una sola clase que está separada de su entorno.
  • Pruebas de aceptación: se utilizan para verificar una sola funcionalidad.

De manera similar, la refactorización también se puede clasificar en diferentes tipos.  

  • Refactorización a nivel de clase local:   las pruebas unitarias para esta clase no han cambiado y las pruebas siguen en verde. Si las pruebas unitarias están diseñadas para verificar escenarios completos en lugar de llamadas de un solo miembro, la refactorización cae en esta categoría.  
  • Refactorización que afecta a varias clases:   en este caso, las pruebas de aceptación son la única forma de verificar la funcionalidad completa después de la refactorización. Esto es útil si se proporciona una nueva forma de funcionalidad y se implementa cambiando clase tras clase y pruebas unitarias asociadas. Un aspecto importante a recordar para la refactorización en TDD es que cuando el código se refactoriza y afecta la interfaz/API, todas las personas que llaman a esta interfaz/API junto con las pruebas escritas para la interfaz/API deben cambiarse como parte de la refactorización. En congruencia con la definición, la refactorización es un cambio de «preservación del comportamiento».

Publicación traducida automáticamente

Artículo escrito por shubhammodi1403 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *