Duplicación de código sin abstracción obvia

13

¿Alguna vez ha encontrado un caso de duplicación de código en el que, al observar las líneas de código, no pudo encajar en una abstracción temática que describa fielmente su papel en la lógica? ¿Y qué hiciste para abordarlo?

Es la duplicación de código, por lo que idealmente necesitamos hacer algo de refractorización, como por ejemplo, hacerla su propia función. Pero como el código no tiene una buena abstracción para describirlo, el resultado sería una función extraña para la que ni siquiera podemos descifrar un buen nombre, y cuyo rol en la lógica no es obvio solo con mirarlo. Eso, para mí, duele la claridad del código. Podemos conservar la claridad y dejarla como está, pero luego dañamos la capacidad de mantenimiento.

¿Cuál crees que es la mejor manera de abordar algo como esto?

    
pregunta EpsilonVector 25.10.2010 - 23:55

4 respuestas

17

A veces, la duplicación de código es el resultado de un "juego de palabras": dos cosas parecen iguales, pero no lo son.

Es posible que el exceso de abstracción pueda romper la verdadera modularidad de su sistema. Bajo el régimen de modularidad, tiene que decidir "¿qué es probable que cambie?" y "¿qué es estable?". Lo que sea estable se pone en la interfaz, mientras que lo que es inestable se encapsula en la implementación del módulo. Luego, cuando las cosas cambian, el cambio que necesita hacer está aislado en ese módulo.

La refactorización es necesaria cuando lo que creías estable (por ejemplo, esta llamada a la API siempre tendrá dos argumentos) debe cambiar.

Entonces, para estos dos fragmentos de código duplicados, me gustaría preguntar: ¿Un cambio requerido en uno significa necesariamente que el otro también debe cambiarse?

La forma en que respondas a esa pregunta podría darte una mejor idea de lo que podría ser una buena abstracción.

Los patrones de diseño también son herramientas útiles. Quizás su código duplicado esté haciendo un recorrido de alguna forma, y se debe aplicar el patrón de iterador.

Si su código duplicado tiene múltiples valores de retorno (y es por eso que no puede hacer un método de extracción simple), tal vez debería crear una clase que contenga los valores devueltos. La clase podría llamar a un método abstracto para cada punto que varía entre los dos fragmentos de código. Luego harías dos implementaciones concretas de la clase: una para cada fragmento. [Este es efectivamente el patrón de diseño del Método de plantilla, que no debe confundirse con el concepto de plantillas en C ++. Alternativamente, lo que está viendo podría resolverse mejor con el patrón de Estrategia.]

Otra forma natural y útil de pensarlo es con funciones de orden superior. Por ejemplo, hacer lambdas o usar clases internas anónimas para que el código pase a la abstracción. En general, puede eliminar la duplicación, pero a menos que realmente exista una relación entre ellos [si uno cambia, el otro también debe] entonces puede estar dañando la modularidad, no ayudándolo.

    
respondido por el Macneil 26.10.2010 - 00:13
4

Cuando te encuentras con una situación como esta, es mejor pensar en abstracciones "no tradicionales". Tal vez tenga mucha duplicación dentro de una función y la factorización de una función simple no encaja muy bien porque tiene que pasar demasiadas variables. Aquí, una función anidada de estilo D / Python (con acceso al ámbito externo) funcionaría muy bien. (Sí, puedes hacer que una clase mantenga todo ese estado, pero si solo lo estás utilizando en dos funciones, esta es una solución fea y detallada para no tener funciones anidadas). Tal vez la herencia no encaja, pero es una Mixin funcionaría bien. Tal vez lo que realmente necesitas es una macro. Tal vez debería considerar alguna metaprogramación de plantillas o reflexión / introspección, o incluso programación generativa.

Por supuesto, desde un punto de vista pragmático, todo esto es difícil, si no imposible, si su idioma no los admite y no tiene suficientes capacidades de metaprogramación para implementarlos de manera limpia dentro del idioma. Si este es el caso, no sé qué decirte excepto "obtener un lenguaje mejor". Además, aprender un lenguaje de alto nivel con muchas capacidades de abstracción (como Ruby, Python, Lisp o D) puede ayudarlo a programar mejor en lenguajes de nivel inferior donde algunas de las técnicas aún podrían ser útiles, pero menos obvias.

    
respondido por el dsimcha 26.10.2010 - 00:13
3

Personalmente lo ignoro y sigo adelante. ¡Lo más probable es que si ese es un caso extraño, es mejor duplicarlo, podrías pasar refactorizaciones por mucho tiempo y el siguiente desarrollador tomará un vistazo y deshará tu cambio!

    
respondido por el Toby 26.10.2010 - 00:04
1

Sin un ejemplo de código, es difícil decir por qué su código no tiene una abstracción fácilmente identificable. Con esa advertencia, aquí hay un par de ideas:

  • en lugar de crear una nueva función para mantener el código común, divida la funcionalidad en varias partes distintas;
  • agrupe las piezas pequeñas según los tipos de datos comunes o el comportamiento abstracto;
  • reescribe el código duplicado dadas las nuevas piezas;
  • Si el nuevo código aún desafía una abstracción clara, divídalo también en pequeño y repita el proceso.

La mayor dificultad en este ejercicio es que es probable que su función incorpore demasiados comportamientos no relacionados en un nivel de abstracción determinado, y necesita manejar algunos de ellos en niveles más bajos. Supones correctamente que la claridad es clave para mantener el código, pero dejar claro el comportamiento del código (su condición actual) es muy diferente de aclarar la intención del código.

Resuma el cómo de las piezas de código más pequeñas haciendo que sus firmas de funciones identifiquen qué, y las piezas más grandes deberían ser más fáciles de clasificar.

    
respondido por el Huperniketes 26.10.2010 - 00:39

Lea otras preguntas en las etiquetas