¿Qué es un argumento de salida, como se menciona en Martin's Clean Code?

14

En la página 45 del Código limpio de Robert C. Martin: Un manual de artesanía ágil de software, Martin escribe que los argumentos de salida deben evitarse. Tengo problemas para entender el significado de "argumento de salida" y por qué deberían evitarse.

El ejemplo de Martin para un argumento de salida appendFooter(s);llama a la función public void appendFooter(StringBuffer report). Su mejora del código esreport.appendFooter();

Tal vez se deba a la falta de contexto de código, pero no veo cómo el uso de argumentos de salida se considera una codificación deficiente. ¿Podría alguien explicar el concepto o dar un ejemplo adicional de código para entender esto?

¿La siguiente función también se consideraría un ejemplo de código inmundo por el principio anterior?

int[] numberArray = {3, 5, 7, 1};
sortArray(numberArray);

Si lo anterior es una violación del principio de Martin de no usar argumentos de salida, ¿sería mejor tener un objeto que tenga una matriz como campo y una función a la que se pueda llamar para ordenar la matriz?

ObjectWithArrayField numberArray = new ObjectWithArrayField(3, 5, 7, 1);
numberArray.sort();
WP0987
fuente

Respuestas:

11

Bob Martin simplemente está hablando de legibilidad .

El problema con el appendFooterejemplo es que si encuentra la línea de código appendFooter(s)en algún lugar de un programa, no es inmediatamente obvio si esa llamada toma scomo entrada y la agrega en algún lugar, o si ssimplemente se pasa para tomar la salida de esa función. Para estar seguro, debe verificar la documentación de la función. Una llamada como report.appendFooter(), sin embargo, evita ese problema: ahora es mucho más obvio lo que sucede.

Sin embargo, tenga en cuenta que Bob Martin no dice "nunca use argumentos de salida", dice "en general, debe evitarlo, porque le ayudará a mantener su código un poco más limpio". Así que esta no es una regla de culto a los cargamentos cerebrales que uno debe seguir ciegamente.

SortLos métodos para las matrices y colecciones estándar son un poco diferentes. Tener el sortmétodo una función miembro de cada tipo de datos de matriz estándar tendría algunos inconvenientes desde el punto de vista del diseñador del lenguaje, por ejemplo, tener un método como Array.sortpermite mantener esto en la biblioteca estándar, fuera del tiempo de ejecución de Java. Pero si crea un tipo de colección individual que a veces tiene que clasificarse, agregar sortcomo función miembro podría ser una mejor idea que ponerlo en una clase separada.

Doc Brown
fuente
2
sortArray(numberArray), por supuesto, se ordena numberArrayen su lugar. ¿O hace una copia de numberArray, ordena la copia y devuelve la copia ordenada sin alterar numberArrayen absoluto?
8bittree
@ 8bittree: eso es cierto, pero ese no es el punto de discusión aquí: un sort()método de contenedor también puede funcionar in situ, sin utilizar un "argumento de salida". Entonces, el hecho de que sortArray(numberArray)sea ​​un método en el lugar no es absolutamente ninguna razón que justifique la "forma de argumento de salida".
Doc Brown
1
Mi punto era más que no es del todo obvio lo que sortArray(numberArray)está haciendo. Puede ser obvio que si no devuelve el mismo tipo que acepta, entonces debe estar en su lugar. Pero sin ver el tipo de retorno, o si el tipo de retorno coincide con el tipo de entrada, no está claro sin mirar la definición.
8bittree
1
@ 8bittree: Ok, me tienes, eliminé la declaración en juego de mi respuesta. Sin embargo, el problema que describe no desaparece al usar una función miembro, incluso una función miembro "ordenar" podría comportarse de esa manera.
Doc Brown
11

Se trata de utilizar un mecanismo inesperado para devolver un valor de la función, que generalmente es el resultado de hacer demasiado en la función o tener responsabilidades desalineadas. Con mucho, la mejor manera de comunicar el resultado de una función es usar el valor de retorno. Espero que sea evidente. En lenguajes orientados a objetos, el segundo mejor método es mutar el objeto.

Entre esas dos opciones, hay tantas formas claras y obvias de comunicar el resultado de una función que si alguna vez desea mutar los argumentos como el único medio, algo ha salido mal en su arquitectura. Debe reorganizar las responsabilidades de su clase para que el que realiza la mutación posea los datos en primer lugar.

La única excepción es para algoritmos muy genéricos. Por ejemplo, un algoritmo de ordenación podría estar separado de los contenedores que ordena, si se puede aplicar genéricamente a cualquier tipo de contenedor utilizando su interfaz pública. Una función de un solo disparo appendFooterno tiene esa excusa.

Karl Bielefeldt
fuente