O que é um argumento de saída, conforme mencionado no Código Limpo de Martin?

14

Na página 45 do Código Limpo de Robert C. Martin: Manual do Artesanato em Software Ágil, Martin escreve que os argumentos de saída devem ser evitados. Estou tendo problemas para entender o significado de "argumento de saída" e por que eles devem ser evitados.

O exemplo de Martin para um argumento de saída appendFooter(s);chama a função public void appendFooter(StringBuffer report). Sua melhoria do código éreport.appendFooter();

Talvez seja devido à falta de contexto do código, mas não vejo como usar argumentos de saída é considerado uma codificação ruim. Alguém poderia explicar o conceito ou dar um exemplo adicional de código para entender isso?

A função a seguir também seria considerada um exemplo de código impuro pelo princípio acima?

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

Se o exposto acima é uma violação do princípio de Martin de não usar argumentos de saída, seria melhor ter um objeto que tenha uma matriz como um campo e uma função que possa ser chamada para classificar a matriz?

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

Respostas:

10

Bob Martin está simplesmente falando sobre legibilidade .

O problema com o appendFooterexemplo é que, se você encontrar a linha de código appendFooter(s)em algum lugar de um programa, não é imediatamente óbvio se essa chamada recebe scomo entrada e a anexa a algum lugar, ou se sé passada apenas para obter a saída dessa função. Para ter certeza, você precisa verificar a documentação da função. Uma ligação como report.appendFooter(), no entanto, evita esse problema: é muito mais óbvio agora o que acontece.

Note, no entanto, Bob Martin não diz "nunca use argumentos de saída", ele diz "em geral, você deve evitá-lo, porque isso ajudará você a manter seu código um pouco mais limpo". Portanto, essa não é uma regra de culto de carga que se deve seguir cegamente.

SortOs métodos para matrizes e coleções padrão são um pouco diferentes. Ter o sortmétodo como uma função membro de cada tipo de dados de matriz padrão teria algumas desvantagens do ponto de vista do criador da linguagem, por exemplo, ter um método como Array.sortpermite manter isso na biblioteca padrão, fora do tempo de execução Java. Mas se você criar um tipo de coleção individual que precise ser classificado às vezes, adicionar sortcomo função de membro pode ser realmente uma idéia melhor do que colocá-lo em uma classe separada.

Doc Brown
fonte
2
sortArray(numberArray), é claro, classifica numberArrayno lugar. Ou ele faz uma cópia numberArray, classifica a cópia e retorna a cópia classificada sem alterar numberArraynada?
8bittree
@ 8bittree: isso é verdade, mas esse não é o ponto de discussão aqui - o sort()método de um contêiner também pode funcionar no local, sem usar um "argumento de saída". Portanto, apenas porque sortArray(numberArray)é um método in-loco, não há absolutamente nenhuma razão que justifique o "formulário de argumento de saída".
Doc Brown
1
Meu argumento era mais que não é totalmente óbvio o que sortArray(numberArray)está fazendo. Pode ser óbvio se ele não retornar o mesmo tipo que aceita, então ele deve estar no lugar. Mas sem ver o tipo de retorno, ou se o tipo de retorno corresponder ao tipo de entrada, não ficará claro sem examinar a definição.
8bittree
1
@ 8bittree: Ok, você me entendeu, eu removi a declaração em jogo da minha resposta. No entanto, o problema que você descreve não desaparece usando uma função de membro - mesmo uma função de membro "classificar" pode se comportar dessa maneira.
Doc Brown
10

É uma questão de usar um mecanismo inesperado para retornar um valor da função, que geralmente é o resultado de fazer muito na função ou ter responsabilidades desalinhadas. De longe, a melhor maneira de comunicar o resultado de uma função é usar o valor de retorno. Espero que seja evidente. Nas linguagens orientadas a objetos, o segundo melhor método é alterar o objeto.

Entre essas duas opções, existem tantas maneiras claras e óbvias de comunicar o resultado de uma função que, se você quiser mudar os argumentos como o único meio, algo deu errado na sua arquitetura. Você precisa reorganizar suas responsabilidades de classe para que quem faz a mutação possua os dados em primeiro lugar.

A única exceção é para algoritmos muito genéricos. Por exemplo, um algoritmo de classificação pode ser legitimamente separado dos contêineres que classifica, se puder ser aplicado genericamente a qualquer tipo de contêiner usando sua interface pública. Uma appendFooterfunção de tiro único não tem essa desculpa.

Karl Bielefeldt
fonte