Técnicas para minimizar o número de argumentos de função

13

No Código Limpo, está escrito que "o número ideal de argumentos para uma função é zero". As razões pelas quais são explicadas e fazem sentido. O que eu estou procurando são técnicas para refatorar métodos com 4 ou mais argumentos para resolver esse problema.

Uma maneira é extrair os argumentos para uma nova classe, mas isso certamente levaria a uma explosão de classes? E é provável que essas classes acabem com nomes que violam algumas das regras de nomenclatura (terminando com "Dados" ou "Informações" etc.)?

Outra técnica é transformar as variáveis ​​usadas por várias funções em uma variável membro privada para evitar que elas sejam transmitidas, mas isso expande o escopo da variável, possivelmente de forma que seja aberta a funções que na verdade não precisam.

Apenas procurando maneiras de minimizar argumentos de função, tendo aceitado os motivos pelos quais é uma boa ideia fazê-lo.

Neil Barnwell
fonte
21
Tbh eu não concordo com o código limpo. Se o número de argumentos para uma função for zero, isso implica que a função tem efeitos colaterais e provavelmente muda de estado em algum lugar. Embora eu concorde que menos de 4 argumentos possa ser uma boa regra geral - eu prefiro ter uma função com 8 argumentos que é estática e sem efeitos colaterais do que uma função não estática com zero argumentos que altera o estado e tem efeitos colaterais .
wasatz 23/09/16
4
" No Código Limpo, está escrito que" o número ideal de argumentos para uma função é zero ". " Sério? Isso é tão errado! O número ideal de parâmetros é um, com um valor de retorno que é derivado deterministicamente desse parâmetro. Na prática, o número de parâmetros não importa muito; o que importa é que, sempre que possível, a função deve ser pura (ou seja, deriva seu valor de retorno apenas de seus parâmetros sem efeitos colaterais).
David Arno
2
Bem, o livro faz mais tarde ir para salientar que os efeitos colaterais não são desejáveis ...
Neil Barnwell
2
Possível duplicado de Estratégias para o parâmetro de embrulho
mosquito

Respostas:

16

A coisa mais importante a lembrar é que essas são diretrizes, não regras.

Há casos em que um método simplesmente deve receber um argumento. Pense no +método para números, por exemplo. Ou o addmétodo para uma coleção.

De fato, pode-se argumentar que o que significa adicionar dois números depende do contexto, por exemplo, em ℤ 3 + 3 == 6, mas em ℤ | 5 3 + 3 == 2 , portanto, o operador de adição deve ser um método em um objeto de contexto que usa dois argumentos em vez de um método em números que leva um argumento.

Da mesma forma, um método para comparar dois objetos deve ser o método de um objeto usando o outro como argumento, ou um método do contexto, usando dois objetos como argumento, portanto, não faz sentido ter um método de comparação com menos de um argumento.

Dito isto, há algumas coisas que podem ser feitas para reduzir o número de argumentos para um método:

  • Torne o método menor : Talvez, se o método precisar de tantos argumentos, esteja fazendo muito?
  • Uma abstração ausente : se os argumentos estão intimamente correlacionados, talvez eles pertençam um ao outro e há uma abstração que está faltando? (Exemplo de livro de texto canônico: em vez de duas coordenadas, passe um Pointobjeto ou, em vez de passar nome de usuário e email, passe um IdCardobjeto.)
  • Estado do objeto : se o argumento for necessário por vários métodos, talvez ele deva fazer parte do estado do objeto. Se for necessário apenas para alguns dos métodos, mas não para outros, talvez o objeto esteja fazendo muito e realmente deva ser dois objetos.

Uma maneira é extrair os argumentos para uma nova classe, mas isso certamente levaria a uma explosão de classes?

Se o seu modelo de domínio tiver muitos tipos diferentes de coisas, seu código terminará com muitos tipos diferentes de objetos. Não há nada de errado nisso.

E é provável que essas classes acabem com nomes que violam algumas das regras de nomenclatura (terminando com "Dados" ou "Informações" etc.)?

Se você não conseguir encontrar um nome adequado, talvez agrupe muitos argumentos ou poucos. Portanto, você possui apenas um fragmento de uma classe ou possui mais de uma classe.

Outra técnica é transformar as variáveis ​​usadas por várias funções em uma variável membro privada para evitar que elas sejam transmitidas, mas isso expande o escopo da variável, possivelmente de forma que seja aberta a funções que na verdade não precisam.

Se você possui um grupo de métodos que funcionam com os mesmos argumentos e outro grupo que não, talvez eles pertençam a classes diferentes.

Observe com que frequência eu usei a palavra "talvez"? É por isso que essas são diretrizes, não regras. Talvez o seu método com 4 parâmetros esteja perfeitamente correto!

Jörg W Mittag
fonte
7
@ BrunoSchäpper: Claro: (1) " Torne o método menor: talvez, se o método precisar de tantos argumentos, esteja fazendo muito? ". O número de parâmetros é um teste ruim disso. Parâmetros opcionais / booleanos e muitas linhas de código são fortes indicadores de que um método está fazendo muito. Muitos parâmetros são, na melhor das hipóteses, um ponto fraco. (2) " Estado do objeto: se o argumento for necessário por vários métodos, talvez ele deva fazer parte do estado do objeto ". Não, não e três vezes, não. Minimize o estado do objeto; parâmetros de função. Se possível, passe um valor para todos os métodos por meio de parâmetros para evitar o estado do objeto.
David Arno
O exemplo que você deu para adição é totalmente errado. A addfunção para números naturais e a addfunção para o anel de números inteiros mod n são duas ações de funções diferentes em dois tipos diferentes. Também não entendo o que você quer dizer com "contexto".
gardenhead 23/09/16
Thx @DavidArno. 1) concordou, não um indicador forte por si só. Mas uma boa, no entanto. Frequentemente vejo alguns métodos, com alguns objetos passados. Não há estado do objeto. Isso é bom, mas 2) melhor opção O IMHO está refatorando esses métodos, mova o estado implícito para uma nova classe, que usa todos esses parâmetros como argumentos explícitos. Você acaba com um método público de argumento zero e muitos métodos internos de argumento zero a um. O estado não é público, global ou mesmo mantido vivo por muito tempo, mas o código é muito mais limpo.
Bruno Schäpper
6

Observe que zero argumentos não implica efeitos colaterais, porque seu objeto é um argumento implícito. Veja quantos métodos de aridade zero a lista imutável de Scala possui, por exemplo.

Uma técnica útil que chamo de técnica de "foco da lente". Quando você focaliza uma lente de câmera, é mais fácil ver o verdadeiro ponto de foco, se você o levar longe demais, depois recoloque-o no ponto correto. O mesmo acontece com a refatoração de software.

Especialmente se você estiver usando o controle de versão distribuído, é fácil experimentar as alterações de software, ver se você gosta da aparência delas e recuar se não gostar, mas por algum motivo as pessoas geralmente parecem relutantes em fazê-lo.

No contexto da sua pergunta atual, isso significa escrever as versões de zero ou um argumento, com várias funções de divisão primeiro, então é relativamente fácil ver quais funções precisam ser combinadas para facilitar a leitura.

Observe que o autor também é um grande defensor do desenvolvimento orientado a testes, que tende a produzir funções de baixa aridade no início porque você começa com seus casos de teste triviais.

Karl Bielefeldt
fonte
1
Como a analogia de "foco da lente" - especialmente ao refatorar, é importante usar a lente grande angular em vez da lente grande plano. E olhando para # de parâmetros é simplesmente muito close-up
tofro
0

Uma abordagem que simplesmente (e ingenuamente - ou devo dizer cegamente ) apenas visa reduzir o número de argumentos para funções provavelmente não está correta. Não há absolutamente nada de errado em funções com um grande número de argumentos. Se eles são exigidos pela lógica, bem, eles são necessários ... Uma longa lista de parâmetros não me preocupa, desde que seja formatada e comentada adequadamente para facilitar a leitura.

No caso de todos ou um subconjunto de argumentos pertencerem a uma entidade lógica única e geralmente serem distribuídos em grupos por todo o programa, talvez faça sentido agrupá-los em algum contêiner - normalmente uma estrutura ou outro objeto. Exemplos típicos podem ser algum tipo de mensagem ou tipo de dados de evento .

Você pode facilmente exagerar nessa abordagem - depois de descobrir que empacotar e descompactar material de e para esses contêineres de transporte gera mais sobrecarga do que melhora a legibilidade, você provavelmente foi longe demais.

OTOH, grandes listas de parâmetros podem ser um sinal de que seu programa pode não estar adequadamente estruturado - talvez a função que requer um número tão grande de parâmetros esteja apenas tentando fazer muito e deva ser dividida em várias funções menores. Prefiro começar aqui do que me preocupar com o número de parâmetros.

tofro
fonte
5
Reduzir cegamente o número de argumentos está errado, é claro. Mas discordo de "Não há absolutamente nada de errado em funções com um grande número de argumentos". . Na minha opinião, quando você atinge uma função com grande número de argumentos, em 99,9% de todos os casos, há uma maneira de melhorar a estrutura do código de maneira deliberada, o que (também) reduz o número de argumentos da função.
Doc Brown
@DocBrown É por isso que existe este último parágrafo, ea recomendação para começar lá .... E outra: Você provavelmente nunca tentou programa contra uma API MS Windows;)
tofro
"talvez a função que requer um número tão grande de parâmetros esteja apenas tentando fazer demais e deva ser dividida em várias funções menores". Eu concordo totalmente, embora, na prática, você não acabe com outra função mais alta na pilha que chama essas várias funções menores? Você pode refatorá-los em um objeto, mas esse objeto terá um ctor. Você pode usar um construtor blá blá blá. O ponto é que é uma regressão infinita - em algum lugar, existem vários valores necessários para que o software faça seu trabalho, e eles precisam acessar essas funções de alguma forma.
Neil Barnwell
1
@NeilBarnwell No caso ideal (vale a pena refatorar), você poderá dividir uma função que precisa de 10 argumentos em três e que precisa de 3-4 argumentos cada. No caso não tão ideal, você acaba com três funções que precisam de 10 argumentos cada (é melhor deixá-lo sozinho). Com relação ao seu argumento de pilha superior: Concordo - pode ser o caso, mas não necessariamente - os argumentos vêm de algum lugar, e essa recuperação também pode estar em algum lugar dentro da pilha e só precisa ser colocada no local apropriado - milhagem tende a variar.
tofro
A lógica do software nunca requer mais de quatro parâmetros. Somente um compilador pode.
TheDoctor
0

Não existe uma maneira mágica: você deve dominar o domínio do problema para descobrir a arquitetura correta. Essa é a única maneira de refatorar: dominar o domínio do problema. Mais de quatro parâmetros são apenas uma aposta certa de que sua arquitetura atual está com defeito e errada.

As únicas exceções são compiladores (metaprogramas) e simulações, onde o limite é teoricamente 8, mas provavelmente apenas 5.

o médico
fonte