Envolver código

La versión actual de la página aún no ha sido revisada por colaboradores experimentados y puede diferir significativamente de la versión revisada el 17 de febrero de 2021; las comprobaciones requieren 7 ediciones .

Smell code (código de olor , código maloliente en inglés  código de olor ) es un término para código con signos (olores) de problemas en el sistema. Fue presentado por Kent Beck [1] y utilizado por Martin Fowler en su libro Refactorización. Mejora del código existente [1] .

Los olores de código  son ​​signos clave de la necesidad de refactorización [2] . Hay olores específicos tanto para los paradigmas de programación como para los lenguajes específicos . El principal problema al que se enfrentan los desarrolladores cuando tratan con olores de código es que los criterios para la oportunidad de la refactorización no se pueden formalizar claramente sin apelar a la estética y al sentido convencional de la belleza. Los olores de código no son un conjunto de reglas claras, sino una descripción de los lugares a los que debe prestar atención al refactorizar [3] . Se detectan fácilmente, pero no en todos los casos indican problemas [1] .

El código de olor conduce a la rotura del código, los desarrolladores deben esforzarse por eliminar los olores mediante la aplicación de refactorizaciones únicas o múltiples [4] . El proceso de refactorización elimina los olores del código, lo que permite que la aplicación continúe evolucionando a la misma o mayor velocidad. La falta de refactorización regular puede paralizar por completo un proyecto con el tiempo, por lo que los olores de código deben eliminarse desde el principio [2] . Hay herramientas para encontrar y corregir olores de código [5] , pero la experiencia muestra que ningún cuadro de mando puede competir con la intuición humana basada en información [6] .

El código huele mal

Olores generales de código orientado a objetos

Duplicación de código

La duplicación de código es el uso de las mismas estructuras de código en varios lugares. La combinación de estas estructuras mejorará el código del programa [6] .

Ejemplos de duplicación y métodos para su eliminación:

  • La misma expresión está presente en dos métodos de la misma clase: debe aplicar el "Método de extracción" y llamar al código del método creado desde ambos puntos;
  • La misma expresión existe en dos subclases en el mismo nivel: debe aplicar el Método de extracción en ambas clases seguido del Método de plantilla de formulario o Campo de extracción, si el código es similar pero no exactamente igual. Si ambos métodos hacen lo mismo usando diferentes algoritmos, puede elegir el más claro de estos algoritmos y aplicar el "Algoritmo de sustitución" (Substitute Algorithm);
  • El código duplicado está en dos clases diferentes: debe aplicar Extraer clase en una clase y luego usar el nuevo componente en otra [6] .
Método largo

Entre los programas de objetos, los programas con métodos cortos viven más tiempo . Cuanto más largo es el procedimiento, más difícil es de entender. Si el método tiene un buen nombre, entonces no necesita mirar su cuerpo [3] .

Se debe seguir una heurística: si siente la necesidad de comentar algo, debe escribir un método. Tiene sentido separar incluso una línea en un método si necesita aclaración [7] .

  • Para reducir el método, basta con aplicar el Método Extract;
  • Si las variables y los parámetros locales impiden la extracción de métodos, puede usar Reemplazar temperatura con consulta, Introducir objeto de parámetro y Conservar todo el objeto [3] ;
  • Las declaraciones condicionales y los bucles indican la posibilidad de separarse en un método separado. Descomponer condicional es adecuado para trabajar con expresiones condicionales. Para trabajar con el ciclo - "Extract Method" (Método de extracción) [7] .
Gran clase

Cuando una clase implementa demasiada funcionalidad, considere subclasificar parte del código. Esto ahorrará a los desarrolladores una cantidad excesiva de atributos que tiene una clase y la duplicación de código [7] .

  • Para reducir una clase, use Extraer clase o Extraer subclase. Al mismo tiempo, se debe prestar atención a la similitud en los nombres de los atributos y si la clase los usa todos al mismo tiempo [3] ;
  • Si la clase grande es una clase de GUI , es posible que desee mover sus datos y comportamiento a un objeto de dominio separado. Sin embargo, puede ser necesario almacenar copias de algunos datos en dos lugares y garantizar su consistencia. Duplicate Observed Data sugiere una forma en que esto se puede hacer [8] .
Larga lista de opciones

Las listas de parámetros largas son difíciles de entender, inconsistentes y difíciles de usar. El uso de objetos permite, en caso de cambios en los datos transmitidos, modificar solo el objeto en sí. Cuando trabaje con objetos, debe pasar lo suficiente para que el método pueda obtener los datos que necesita [8] .

  • "Reemplazar parámetro con método" se usa cuando puede obtener datos llamando a un método en un objeto. Este objeto puede ser un campo u otro parámetro.
  • Preserve Whole Object le permite tomar un grupo de datos recibidos de un objeto y reemplazarlo con el objeto mismo.
  • "Introducir objeto de parámetro" se utiliza si hay varios elementos de datos sin un objeto lógico [8] .
Modificaciones divergentes

El problema surge cuando, al modificar el sistema, es imposible asignar un lugar específico que necesita ser cambiado. Esto es consecuencia de una estructura de software deficiente [8] o de una programación de copiar y pegar .

  • Si es necesario cambiar el conjunto de métodos cada vez que se realizan ciertas modificaciones en el código, entonces se aplica Extract Class (por ejemplo, tres métodos cambian cada vez que se conecta una nueva base de datos y cuatro cuando se agrega un instrumento financiero) [3 ] .
Tiro con escopeta

Cualquier modificación implica muchos pequeños cambios en un gran número de clases. Shotgun es similar a Divergent Modification, pero es lo contrario. Una modificación divergente ocurre cuando hay una clase que realiza muchos cambios diferentes, mientras que Shotgun es un cambio que afecta a muchas clases [9] .

  • Mover todos los cambios a una clase permitirá "Mover método" (Método de movimiento) y "Mover campo" (Mover campo);
  • Si no hay una clase adecuada, se debe crear una nueva clase;
  • Si es necesario, use Inline Class [3] .
Funciones de envidia

El método accede a los datos de otro objeto con más frecuencia que a sus propios datos [3] .

  • "Mover método" se usa si el método necesita moverse explícitamente a otra ubicación;
  • Extraer método se aplica a una parte del método solo si esa parte accede a los datos de otro objeto;
  • El método utiliza las funciones de varias clases: se determina qué clase contiene la mayor cantidad de datos, y el método se coloca en la clase junto con estos datos, o utilizando el método de extracción, el método se divide en varias partes y se colocan en diferentes lugares [10] .

Una regla general fundamental es que las cosas que cambian al mismo tiempo deben mantenerse en un solo lugar. Los datos y las funciones que utilizan esos datos suelen cambiar juntos, pero hay excepciones [10] .

Grupos de datos

Los grupos de datos que ocurren juntos deben convertirse en una clase separada [10] .

  • "Método de extracción" se utiliza para los campos;
  • "Introducir objeto de parámetro" o "Conservar todo el objeto" para parámetros de método [11] .

Una buena prueba es eliminar uno de los valores de los datos y ver si los demás aún tienen sentido. De lo contrario, es una señal segura de que los datos solicitan fusionarse en un objeto [10] .

Obsesión por los tipos elementales

El problema está relacionado con el uso de tipos elementales en lugar de objetos pequeños para tareas pequeñas, como moneda, rangos, cadenas especiales para números de teléfono, etc.

  • "Reemplazar valor de datos con objeto";
  • "Reemplazar una matriz con un objeto" (Reemplazar matriz con objeto);
  • Si es un código de tipo, use Reemplazar código de tipo con clase, Reemplazar código de tipo con subclases o Reemplazar código de tipo con estado/estrategia) [3] .
cambiar sentencias

Una característica obvia del código orientado a objetos es el uso relativamente poco frecuente de sentencias switch (o case) . A menudo, el mismo bloque de interruptores termina disperso en diferentes lugares del programa. Al agregar una nueva opción, debe buscar todos estos bloques de interruptores y modificarlos. Como regla general, cuando observe un bloque de cambio, debe pensar en el polimorfismo [12] .

  • Si el interruptor está cambiando por código de tipo, debe usar "Reemplazar código de tipo con subclases" o "Reemplazar código de tipo con estado/estrategia";
  • Es posible que necesite "Extraer método" y "Mover método" para aislar el interruptor y colocarlo en la clase correcta;
  • Después de configurar la estructura de herencia, debe usar Reemplazar condicional con polimorfismo [3] .
Jerarquías de herencia paralelas

En código con este olor, cada vez que creas una subclase de una de las clases, tienes que crear una subclase de otra clase [12] .

  • Una estrategia común de deduplicación es hacer que las instancias de una jerarquía se refieran a instancias de otra jerarquía y luego eliminar la jerarquía en la clase de referencia mediante Move Method y Move Field [12] .
Clase perezosa

Una clase cuyos costos de existencia no estén cubiertos por las funciones que realiza debe ser eliminada [12] .

  • Si hay subclases con funcionalidad insuficiente, intente Collapse Hierarchy;
  • Los componentes casi inútiles deben someterse a Inline Class [12] .
Generalidad teórica

Este caso se da cuando, en algún momento de la vida de un programa, se proporciona un conjunto de mecanismos que alguna funcionalidad futura puede necesitar. Como resultado, el programa se vuelve más difícil de entender y mantener [13] .

  • Para clases abstractas no utilizadas, utilice Contraer jerarquía;
  • La delegación innecesaria se puede eliminar usando Inline Class;
  • Los métodos con parámetros no utilizados deben someterse a "Eliminar parámetro" [3] .
Campo de tiempo

Los campos temporales son campos que un objeto necesita solo bajo ciertas circunstancias. Este estado de cosas es difícil de entender, ya que se espera que un objeto necesite todos sus campos [14] .

  • Los campos temporales y todo el código que trabaje con ellos deben colocarse en una clase separada usando Extraer clase;
  • Puede eliminar el código ejecutable condicionalmente mediante Introducir objeto nulo para crear un componente alternativo [13] .
Cadena de llamadas

Una cadena de llamadas ocurre cuando un cliente solicita otro objeto de un objeto, otro objeto solicita otro objeto, etc. Tales secuencias de llamadas significan que el cliente está asociado con la navegación en la estructura de clases. Cualquier cambio en los enlaces intermedios implica la necesidad de modificar el cliente [13] .

  • Para eliminar la cadena de llamadas se utiliza la técnica Hide Delegate [13] .
Intermediario

El uso excesivo de la delegación puede conducir a clases en las que la mayoría de los métodos consisten únicamente en llamar a un método de otra clase [13] .

  • Si una clase delega la mayoría de los métodos a otra clase, debe usar "Eliminar intermediario" [15] .
Proximidad fuera de lugar

La "proximidad fuera de lugar" ocurre cuando las clases están más a menudo de lo que deberían estar inmersas en partes cerradas unas de otras [15] .

  • Puede deshacerse de la "Proximidad inapropiada" utilizando el "Método de movimiento" (Método de movimiento) y "Campo de movimiento" (Campo de movimiento);
  • Si es posible, se debe recurrir a "Cambiar Asociación Bidireccional a Unidireccional", "Extraer Clase" o usar "Ocultar Delegado" [15] .
Clases alternativas con diferentes interfaces

Dos clases en las que parte de la funcionalidad es común, pero los métodos que la implementan tienen parámetros diferentes [16] .

  • Aplicar "Cambiar nombre de método" a todos los métodos que realizan las mismas acciones pero difieren en las firmas [15] .
Clase de biblioteca incompleta

Las bibliotecas dejan de cumplir con los requisitos de los usuarios después de un tiempo. La solución natural es cambiar algunas cosas en las bibliotecas, pero no cambiar las clases de la biblioteca. Debe usar métodos de refactorización diseñados específicamente para este propósito [16] .

  • Si necesita agregar un par de métodos, use "Introducir método extranjero";
  • Si necesita cambiar seriamente el comportamiento de la clase, use "Introduce Local Extension" (Introducir extensión local) [16] .
Clases de datos

Las clases de datos son clases que contienen solo campos y métodos para acceder a ellos, son simplemente contenedores de datos utilizados por otras clases [16] .

  • Aplicar Encapsular campo y Encapsular colección [3] .
Renuncia a la herencia

Si el hijo usa solo una pequeña parte de los métodos y propiedades heredados del padre, esto es un signo de una mala jerarquía.

  • Debe crear una nueva clase en el mismo nivel que el niño y usar el método Push Down y el campo Push Down para insertar todos los métodos inactivos en ella. Esto asegura que la clase principal contenga solo lo que se comparte [17] .
Comentarios

A menudo, los comentarios juegan el papel de un "desodorante" del código, que aparece solo porque el código es malo. Cuando sienta la necesidad de escribir un comentario, intente reestructurar su código para que cualquier comentario se vuelva redundante [17] .

  • Si aún necesita un comentario para explicar las acciones del bloque, intente usar el método de extracción;
  • Si un método ya está resaltado, pero aún necesita un comentario para explicar su funcionamiento, use Renombrar método;
  • Si desea establecer algunas reglas sobre el estado requerido del sistema, use Introducir aserción [17] .

Véase también

  • antipatrón
  • Categoría:Principios de programación
  • Herramientas de análisis de código estático

Notas

  1. 1 2 3 Martín, 1999 .
  2. 1 2 Vigoroso Hive_CodeSmell .
  3. 1 2 3 4 5 6 7 8 9 10 11 Código con mal olor .
  4. Counsell_Code Smells, 2010 .
  5. devconf .
  6. 1 2 3 Martin Fowler_Refactoring, 2003 , p. 54.
  7. 1 2 3 Martin Fowler_Refactoring, 2003 , p. 55.
  8. 1 2 3 4 Martin Fowler_Refactorización, 2003 , p. 56.
  9. Martin Fowler_Refactorización, 2003 , p. 56-57.
  10. 1 2 3 4 Martin Fowler_Refactorización, 2003 , p. 57.
  11. Código maloliente , pág. 57.
  12. 1 2 3 4 5 Martin Fowler_Refactorización, 2003 , p. 58.
  13. 1 2 3 4 5 Martin Fowler_Refactorización, 2003 , p. 59.
  14. Campo temporal .
  15. 1 2 3 4 Martin Fowler_Refactorización, 2003 , p. 60
  16. 1 2 3 4 Refactorización de código .
  17. 1 2 3 Martin Fowler_Refactoring, 2003 , p. 61.

Literatura

  • Fowler, M. Capítulo 3. Código maloliente // Refactorización. Mejora del código existente = Refactorización: Mejora del diseño del código existente / Per. De inglés. S. Makkaveeva. - 1ª ed. - San Petersburgo. : Símbolo-Plus, 2003. - S. 54-62. - 432 pág. — ISBN 5-93286-045-6 .

Enlaces

  • Campo Temporal . codificacióncraft.ru Recuperado: 5 de noviembre de 2013.
  • CodeSmell  (inglés) . www.martinfowler.com. Consultado: 13 de octubre de 2013.
  • Código Olor  (Inglés) . Cunningham & Cunningham Inc. (c2.com). Recuperado: 23 de noviembre de 2013.