¿Es este un buen patrón: reemplazar una función larga con una serie de lambdas?

14

Recientemente me encontré con la siguiente situación.

class A{
public:
    void calculate(T inputs);
}

En primer lugar, A representa un objeto en el mundo físico, que es un argumento sólido para no dividir la clase. Ahora, calculate() resulta ser una función bastante larga y complicada. Percibo tres posibles estructuras para ello:

  • escríbalo como un muro de texto - ventajas - toda la información está en un solo lugar
  • escriba las funciones de la utilidad private en la clase y úselas en el cuerpo de calculate - desventajas - el resto de la clase no sabe / cuida / entiende sobre esos métodos
  • escriba calculate de la siguiente manera:

    void A::calculate(T inputs){    
        auto lambda1 = () [] {};    
        auto lambda2 = () [] {};    
        auto lambda3 = () [] {};
    
        lambda1(inputs.first_logical_chunk);
        lambda2(inputs.second_logical_chunk);
        lambda3(inputs.third_logical_chunk);
    }
    

¿Se puede considerar esto como una buena o mala práctica? ¿Este enfoque revela algún problema? En general, ¿debería considerar esto como un buen enfoque cuando me enfrento nuevamente con la misma situación?

EDITAR:

class A{
    ...
public:
    // Reconfiguration of the algorithm.
    void set_colour(double colour);
    void set_density(double density);
    void set_predelay(unsigned long microseconds);
    void set_reverb_time(double reverb_time, double room_size);
    void set_drywet(double left, double right);
    void set_room_size(double value);;

private:
    // Sub-model objects.
    ...
}

Todos esos métodos:

  • obtener un valor
  • calcular algunos otros valores, sin usar el estado
  • llame a algunos de los "objetos de submodelo" para cambiar su estado.

Resulta que, exceptuando set_room_size() , esos métodos simplemente pasan el valor solicitado a los subobjetos. set_room_size() , por otro lado, hace un par de pantallas de fórmulas oscuras y luego (2) hace la mitad de una pantalla de llamadores de subobjetos para aplicar los diversos resultados obtenidos. Por lo tanto, he separado la función en dos lambdas y las llamo al final de la función. Si hubiera podido dividirlo en partes más lógicas, habría aislado más lambdas.

En cualquier caso, el objetivo de la pregunta actual es determinar si esa forma de pensar debe persistir o, en el mejor de los casos, no agrega valor (legibilidad, capacidad de mantenimiento, capacidad de depuración, etc.).

    
pregunta Vorac 27.01.2015 - 12:07

2 respuestas

13

No, este no es generalmente un buen patrón

Lo que estás haciendo es dividir una función en funciones más pequeñas usando lambda. Sin embargo, hay una herramienta mucho mejor para dividir funciones: funciones.

Los Lambdas funcionan, como has visto, pero significan mucho, mucho, mucho más que simplemente dividir una función en bits locales. Lambdas hacer:

  • Cierres. Puede utilizar variables en el ámbito exterior dentro de la lambda. Esto es muy poderoso y muy complicado.
  • Reasignación. Si bien su ejemplo hace que sea difícil realizar una asignación, uno siempre tiene que prestar atención a la idea de que el código puede intercambiar funciones en cualquier momento.
  • Funciones de primera clase. Puede pasar una función lambda a otra función, haciendo algo conocido como "programación funcional".

En el instante en que incorporas lambdas a la mezcla, el siguiente desarrollador que revise el código debe cargar de manera mental todas esas reglas para ver cómo funciona tu código. No saben que no vas a usar toda esa funcionalidad. Esto es muy caro, en comparación con las alternativas.

Es como usar una pala para hacer la jardinería. Sabes que solo lo estás utilizando para cavar pequeños agujeros para las flores de este año, pero los vecinos se pondrán nerviosos.

Considere que todo lo que está haciendo es agrupar visualmente su código fuente. Al compilador en realidad no le importa que hayas comido cosas con lambdas. De hecho, esperaría que el optimizador deshaga inmediatamente todo lo que acabas de hacer cuando se compila. Está meramente atendiendo al siguiente lector (¡Gracias por hacerlo, incluso si no estamos de acuerdo con la metodología! ¡El código se lee con mucha más frecuencia de lo que está escrito!). Todo lo que estás haciendo es agrupar la funcionalidad.

  • Las funciones ubicadas localmente dentro del flujo del código fuente harían lo mismo, sin invocar a lambda. Una vez más, lo único que importa es que el lector pueda leerlo.
  • Los comentarios en la parte superior de la función que dicen "estamos dividiendo esta función en tres partes", seguidos por grandes líneas largas de // ------------------ entre cada parte.
  • También puede colocar cada parte del cálculo en su propio ámbito. Esto tiene la ventaja de probar inmediatamente, sin lugar a dudas, que no hay un intercambio de variables entre las partes.

EDITAR: Al ver su edición con código de ejemplo, me inclino por que la notación de comentario sea la más limpia, con corchetes para reforzar los límites que sugieren los comentarios. Sin embargo, si alguna de las funciones era reutilizable en otras funciones, recomendaría usar funciones en su lugar

void A::set_room_size(double value)
{
    {
        // Part 1: {description of part 1}
        ...
    }
    // ------------------------
    {
        // Part 2: {description of part 2}
        ...
    }
    // ------------------------
    {
        // Part 3: {description of part 3}
        ...
    }
}
    
respondido por el Cort Ammon 27.01.2015 - 16:29
20

Creo que hiciste una mala suposición:

  

En primer lugar, A representa un objeto en el mundo físico, lo cual es un argumento sólido para no dividir la clase.

No estoy de acuerdo con esto. Por ejemplo, si tuviera una clase que representa un automóvil, definitivamente me gustaría dividirlo, porque seguramente quiero que una clase más pequeña represente los neumáticos.

Deberías dividir esta función en funciones privadas más pequeñas. Si realmente parece estar separado de la otra parte de la clase, entonces eso puede ser una señal de que la clase debe estar separada. Por supuesto, es difícil decirlo sin un ejemplo explícito.

Realmente no veo la ventaja de usar funciones lambda en este caso, porque realmente no hace que el código sea más limpio. Fueron creados para ayudar a la programación de estilo funcional, pero esto no es eso.

Lo que escribiste se parece un poco a los objetos de funciones anidadas de estilo Javascript. Lo que de nuevo es una señal de que están muy unidos. ¿Estás seguro de que no deberías hacer una clase separada para ellos?

Para resumir, no creo que este sea un buen patrón.

ACTUALIZAR

Si no ve ninguna forma de encapsular esta funcionalidad en una clase significativa, puede crear funciones de ayuda de ámbito de archivos, que son no miembros de su clase. Esto es C ++, después de todo, el diseño OO no es obligatorio.

    
respondido por el Gábor Angyal 27.01.2015 - 12:42

Lea otras preguntas en las etiquetas