c++ - retorno - tipos de funciones en programacion
Manera elegante de pasar múltiples argumentos a una función (6)
Ponlos en una struct
Crea una estructura
struct GenerateScriptParams { /* ... */ };
y poner todos los parámetros allí. También puede proporcionar valores predeterminados para la inicialización de la struct
implementando un constructor predeterminado o, en C ++ 11, proporcionando la inicialización predeterminada de miembros individuales. A continuación, puede cambiar los valores que se supone que no están predeterminados. Este picking selectivo de parámetros no predeterminados no es posible para una llamada a función con muchos parámetros en C ++.
Hacer la interfaz agradable para quien llama
Sin embargo, el uso es un poco feo, ya que tiene que crear un objeto de nombre temporal, luego cambiar los valores que no deberían ser predeterminados y luego pasar el objeto a la función:
GenerateScriptParams gsp;
gsp.net = true;
gsp.phone = false;
gps.extra = 10;
generate_script( gsp );
Si llama a esa función en varios lugares diferentes, tiene sentido evitar esta fealdad al proporcionar funciones de miembros mutantes que pueden encadenarse:
GenerateScriptParams & GenerateScriptParams::setNet ( bool val );
GenerateScriptParams & GenerateScriptParams::setTV ( bool val );
GenerateScriptParams & GenerateScriptParams::setPhone( bool val );
// ... //
Entonces, el código de llamada puede escribir
generate_script( GenerateScriptParams()
.setNet(true),
.setPhone(false),
.setExtra(10) );
sin la fealdad anterior Esto evita el objeto nombrado que solo se usa una vez.
Tengo una función que se ve así:
bool generate_script (bool net, bool tv, bool phone,
std::string clientsID,
std::string password,
int index, std::string number,
std::string Iport, std::string sernoID,
std::string VoiP_number, std::string VoiP_pass,
std::string target, int slot, int port,
int onu, int extra, std::string IP, std::string MAC);
En mi opinión, se ve feo. ¿Cuál es la forma correcta de manejar este problema? ¿Debo crear algunos vectores con diferentes tipos de datos (int, string y bool) y pasarlos como argumentos a esta función?
Ignorando la posibilidad o la conveniencia de cambiar la función o programa de alguna manera para reducir el número de parámetros ...
He visto estándares de codificación que especifican cuánto tiempo deben formatearse las listas de parámetros, para los casos en que la refactorización no es posible. Un ejemplo de esto es usar sangrías dobles y un parámetro por línea (no para todas las funciones, solo para aquellas que tienen líneas múltiples de parámetros).
P.ej
bool generate_script (
bool net,
bool tv,
bool phone,
std::string clientsID,
std::string password,
int index,
std::string number,
std::string Iport,
std::string sernoID,
std::string VoiP_number,
std::string VoiP_pass,
std::string target,
int slot,
int port,
int onu,
int extra,
std::string IP,
std::string MAC);
El punto aquí es crear un diseño consistente y buscar todas las funciones con una gran cantidad de parámetros.
O podrías usar una interfaz fluida . Se vería así:
script my_script(mandatory, parameters);
my_script.net(true).tv(false).phone(true);
Esto es aplicable si tiene valores predeterminados para sus parámetros especificados o está permitido tener un script parcialmente construido.
Personalmente, no creo que mover todos los argumentos en una estructura haga que el código sea mucho mejor. Solo mueve la suciedad debajo de la alfombra. Cuando va a tratar con la creación de la estructura, tiene el mismo problema.
La pregunta es qué tan reutilizable será esta estructura. Si termina con 18 parámetros para una función, llame a algo que no está del todo bien en su diseño. Después de un análisis posterior, puede descubrir que esos parámetros pueden agruparse en varias clases diferentes y esas clases se pueden agregar a un único objeto que será la entrada de su función. También puede preferir clases para estructurar para proteger sus datos.
EDITAR
Le daré un pequeño ejemplo para describir por qué varias clases son mejores que una estructura monolítica. Comencemos a contar las pruebas que necesita escribir para cubrir la función anterior. Hay 18 parámetros como entrada (3 booleanos). Así que vamos a necesitar al menos 15 pruebas solo para validar la entrada (suponiendo que los valores no están interconectados).
El número total de pruebas es imposible de calcular sin la implementación, pero podemos tener una idea de la magnitud. Deje tomar el límite inferior, toda la entrada se puede tratar como booleana, el número de combinaciones posibles es 2 ^ 18, por lo tanto, alrededor de 262000 pruebas .
Ahora, ¿qué pasa si dividimos la entrada en varios objetos?
En primer lugar, el código para validar la entrada se aleja de la función para el cuerpo de cada objeto (y puede reutilizarse).
Pero lo más importante es que la cantidad de pruebas se colapsará, digamos en un grupo de cuatro (4,4,4 y 4 params por objeto) el número total de pruebas es solo:
2 ^ 4 + 2 ^ 4 + 2 ^ 4 + 2 ^ 4 + 2 ^ 4 = 80
El quinto atributo se debe a la permutación de los objetos consigo mismos.
Entonces, ¿qué es más costoso? Escribir miles de pruebas o pocas clases más?
Obviamente, esta es una simplificación cruda, sin embargo, subyace al núcleo del problema. Una interfaz de desorden no es solo cuestión de estilo o un inconveniente para el desarrollador; es un verdadero impedimento para producir código de calidad .
Esta es la lección más importante que aprendí en mi carrera como desarrollador profesional: "Las grandes clases y las interfaces gordas son malas". Esa es solo mi versión heurística del principio de responsabilidad única (me doy cuenta de que el SRP puede ser complicado para hacer las cosas bien, lo que parece razonable es responsabilidad única, puede no ser el mismo después de una hora de codificación, así que usé heurística regla para ayudarme a revaluar mis elecciones iniciales).
Si todos estos parámetros están relacionados de manera significativa, empaquételos en una estructura.
Un poco tarde aquí, pero como nadie lo ha hecho aún, me gustaría señalar un aspecto obvio del problema: para mí, una función que requiere tantos argumentos es probable que haga muchos cálculos, así que considere la posibilidad de descomponerlo en funciones más pequeñas como primer paso.
Esto debería ayudarlo a estructurar sus datos.