Quantos parâmetros são muitos? [fechadas]

228

Rotinas podem ter parâmetros, isso não é novidade. Você pode definir quantos parâmetros precisar, mas muitos deles dificultam a compreensão e a manutenção de sua rotina.

Obviamente, você pode usar uma variável estruturada como solução alternativa: colocar todas essas variáveis ​​em uma única estrutura e passá-la para a rotina. De fato, o uso de estruturas para simplificar as listas de parâmetros é uma das técnicas descritas por Steve McConnell no Code Complete . Mas como ele diz:

Programadores cuidadosos evitam agrupar dados mais do que é logicamente necessário.

Portanto, se sua rotina possui muitos parâmetros ou você usa uma estrutura para disfarçar uma grande lista de parâmetros, provavelmente está fazendo algo errado. Ou seja, você não está mantendo o acoplamento solto.

Minha pergunta é: quando posso considerar uma lista de parâmetros muito grande? Eu acho que mais de 5 parâmetros, são muitos. O que você acha?

Auron
fonte
1
Apenas fazendo referência aqui esta questão, que é um exemplo prático de como muitos parâmetros são muitos ...
Gideon
5
No JavaScript 65537 os parâmetros são muitos: jsfiddle.net/vhmgLkdm
Aadit M Shah 11/16/16
basta olhar para o Apache HTTP componentes é quase impossível construir algo dinâmico fora dele
Andrew Scott Evans

Respostas:

162

Quando é que algo considerado tão obsceno que pode ser regulamentado, apesar da garantia da 1ª Emenda à liberdade de expressão? Segundo o juiz Potter Stewart, "eu sei quando vejo". O mesmo vale aqui.

Eu odeio fazer regras rígidas e rápidas como essa, porque a resposta muda não apenas dependendo do tamanho e do escopo do seu projeto, mas acho que muda até o nível do módulo. Dependendo do que o seu método está fazendo ou do que a classe deve representar, é bem possível que 2 argumentos sejam muitos e sejam um sintoma de muito acoplamento.

Eu sugeriria que, fazendo a pergunta em primeiro lugar e qualificando sua pergunta tanto quanto você, você realmente sabe tudo isso. A melhor solução aqui não é confiar em um número rígido e rápido, mas procurar análises de projeto e de código entre seus pares para identificar áreas em que você tem baixa coesão e acoplamento rígido.

Nunca tenha medo de mostrar a seus colegas o seu trabalho. Se você tem medo, esse é provavelmente o sinal maior de que algo está errado com o seu código e que você já o conhece .

Nick
fonte
Uma boa regra geral é o número de registros da CPU, porque mais e o compilador será forçado a alocá-los na pilha.
Michaelangel007
1
@ Michaelangel007 em relação à resposta que você está comentando, você não deve dizer isso, porque indica que não há regra. Além disso, o número de parâmetros é uma questão de legibilidade, não de desempenho.
Clemp6r
1
@ clemp6r Incorreto - são os dois . Os compiladores precisam lidar com o "derramamento de registro" o tempo todo. A única resposta autorizada é inspecionar o assembly do que seu compilador está gerando. O número de registros não muda magicamente na mesma plataforma. pt.wikipedia.org/wiki/Register_allocation
Michaelangel007
124

Uma função só pode ter muitos parâmetros se alguns deles forem redundantes. Se todos os parâmetros forem utilizados, a função deverá ter o número correto de parâmetros. Tome esta função frequentemente usada:

HWND CreateWindowEx
(
  DWORD dwExStyle,
  LPCTSTR lpClassName,
  LPCTSTR lpWindowName,
  DWORD dwStyle,
  int x,
  int y,
  int nWidth,
  int nHeight,
  HWND hWndParent,
  HMENU hMenu,
  HINSTANCE hInstance,
  LPVOID lpParam
);

São 12 parâmetros (9 se você agrupar x, y, weh como um retângulo) e também existem parâmetros derivados do nome da classe. Como você reduziria isso? Deseja reduzir o número mais ao ponto?

Não deixe que o número de parâmetros o incomode, apenas verifique se é lógico e bem documentado e deixe que o intellisense * o ajude.

* Outros assistentes de codificação estão disponíveis!

Skizz
fonte
37
Estou votando nisso. Estou impressionado com todas as outras respostas dizendo "3" e "4"! A resposta correta é: o mínimo necessário, que às vezes pode ser bastante.
6138 Tony Andrews
16
Se essa função fosse projetada hoje, provavelmente pareceria um pouco diferente. x, y, nWidth e nHeight podem ser agrupados em um objeto Rectangle. style e xStyle podem ser combinados em um conjunto de enums ou strings. Agora você tem apenas 8 parâmetros.
6118 finnw
16
@finnw Se essa função foi projetada hoje? Já foi redesenhado. Formulário f = novo Formulário (); possui 0 parâmetros.
Nick
36
Este é C e Winapi. Não consigo pensar em um exemplo pior.
L̲̳̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ 13/10
17
Não não. Este é o estilo processual. Windows são objetos, então isso é obsoleto agora. Hoje, isso seria resolvido com classes agregadas, não com muitos parâmetros. A regra intuitiva do bom design é que, se você não pode descrever a função (incluindo parâmetros) em uma frase simples, ela é mal projetada . Eu acredito que este é o caso.
Jan Turon
106

No Código Limpo , Robert C. Martin dedicou quatro páginas ao assunto. Aqui está a essência:

O número ideal de argumentos para uma função é zero (niládico). A seguir vem um (monádico), seguido de perto por dois (diádico). Três argumentos (triádicos) devem ser evitados sempre que possível. Mais de três (poládicos) exigem justificativas muito especiais - e, portanto, não devem ser usadas de qualquer maneira.

Patrick McElhaney
fonte
2
Duvido que inclua "this" porque esse é o contexto da execução. Na programação funcional, o contexto é global e, oop, o contexto é o objeto no qual você está aplicando o método. Além disso, se você incluir "this" na lista de parâmetros, seria impossível ter 0 params (ideal).
24420 Tom
1
Não, não inclui "isso".
Patrick McElhaney
20
Além disso, Martin deve conversar com o comitê padrão do C ++. Metade do <algorithm> leva mais de 3 parâmetros, porque apenas um intervalo do iterador já é 2. É apenas a natureza da programação com iteradores (isto é, programação genérica com coleções STL).
9608 Steve Joplin
3
@ SteveJessop: a falha do <algorithm> é que ele sempre funciona em uma fatia. Se o algoritmo e a coleção fossem reprojetados, eu faria com que os algoritmos sempre levassem uma coleção inteira, e você teria uma maneira de cortar uma exibição de uma coleção para permitir que algoritmos funcionassem em partes; como alternativa, eu também definiria uma substituição abreviada para facilitar o trabalho no caso comum.
Lie Ryan
3
@ LieRyan: enquanto que, se eu estivesse redesenhando <algorithm>, agisse em um objeto de alcance. Coleções seriam intervalos, mas nem todos os intervalos seriam coleções. E, de fato, o impulso já fez isso. De qualquer forma, o que quero dizer é que uma biblioteca amplamente usada ignora esse conselho; portanto, o pior que lhe é garantido, se você também fizer isso, é que muitos de seus milhões de usuários mexerão com pequenas simplificações em sua interface ;-)
Steve Jessop
79

Alguns códigos com os quais trabalhei no passado usavam variáveis ​​globais apenas para evitar a passagem de muitos parâmetros.

Por favor, não faça isso!

(Usualmente.)

Jeffrey L Whitledge
fonte
Algum código em que eu estava trabalhando utilizava os alunos para obter a mesma coisa. Naquela época, eu era programador C e perguntava: por que existem tantas variáveis ​​globais, por que as entradas / saídas não podem ser parâmetros explicitamente?
user3528438
38

Se você começar a contar mentalmente os parâmetros na assinatura e combiná-los com a chamada, é hora de refatorar!

Rob Walker
fonte
Essa é uma resposta muito boa. Se os parâmetros são organizados logicamente (x, y, w, h), é fácil lembrar de todos eles na ordem correta. É muito mais difícil lembrar onde colocar o ponteiro FILE em putc (que possui apenas dois parâmetros), especialmente porque fprintf é o oposto.
user877329
Melhor resposta. Muito bem dito #
181 Anwar
31

Muito obrigado por todas as suas respostas:

  • Foi um pouco surpreendente encontrar pessoas que também pensam (como eu) que 5 parâmetros são um bom limite para a sanidade do código.

  • Geralmente, as pessoas tendem a concordar que um limite entre 3 e 4 é uma boa regra geral. Isso é razoável, pois as pessoas costumam ter problemas contando mais de 4 coisas.

  • Como aponta Milão , em média as pessoas podem manter mais ou menos 7 coisas na cabeça por vez. Mas acho que você não pode esquecer que, ao projetar / manter / estudar uma rotina, você deve ter em mente mais coisas do que apenas parâmetros.

  • Algumas pessoas consideram que uma rotina deve ter tantos argumentos quanto necessário. Concordo, mas apenas em alguns casos específicos (chamadas para APIs do SO, rotinas em que a otimização é importante etc.). Sugiro ocultar a complexidade dessas rotinas adicionando uma camada de abstração logo acima dessas chamadas sempre que possível.

  • usuario tem algumas idéias interessantes sobre isso. Se você não quiser ler os comentários dele, resumo para você: em poucas palavras, depende :

    Eu odeio fazer regras rígidas e rápidas como essa, porque a resposta muda não apenas dependendo do tamanho e do escopo do seu projeto, mas acho que muda até o nível do módulo. Dependendo do que o seu método está fazendo ou do que a classe deve representar, é bem possível que 2 argumentos sejam muitos e sejam um sintoma de muito acoplamento.

    A moral aqui é não ter medo de mostrar seu código aos colegas, discutir com eles e tentar "identificar áreas onde você tem baixa coesão e acoplamento rígido" .

  • Finalmente, acho que wnoise concorda muito com Nick e conclui sua contribuição satírica com essa visão poética (veja os comentários abaixo) da arte da programação:

    Programar não é engenharia. A organização do código é uma arte porque depende de fatores humanos, que dependem muito do contexto para qualquer regra rígida.

Auron
fonte
16

Esta resposta assume um idioma OO. Se você não estiver usando um - pule esta resposta (em outras palavras, não é uma resposta independente do idioma).

Se você estiver passando mais de três parâmetros (principalmente tipos / objetos intrínsecos), não é "muitos", mas pode haver uma chance de criar um novo objeto.

Procure grupos de parâmetros passados ​​para mais de um método - mesmo um grupo passado para dois métodos quase garante que você deva ter um novo objeto lá.

Então você refatora a funcionalidade em seu novo objeto e não acredita no quanto isso ajuda seu código e seu entendimento da programação OO.

Bill K
fonte
Por favor, nomeie um idioma que não use rotinas nem parâmetros. Eu não consigo pensar em um, mesmo montagem pode ser considerada como tendo rotinas (etiquetas).
Auron
O assembly x86 definitivamente possui rotinas (chamada / retorno). Outros podem exigir combos cmp / jmp.
Brian Knoblauch
Desculpe, "ret", não "return". Suspiros. Já faz um longo dia.
Brian Knoblauch
Desculpe pelo fraseado ambíguo de Auron, acho que o consertei. Foi a minha resposta que foi específica do idioma, não a pergunta.
Bill K
1
Nem todos os idiomas permitem a passagem de estruturas. Além disso, passar uma estrutura significa que o método de recebimento precisa ter o código para manipular a estrutura e, portanto, pode precisar de mais parâmetros. Além disso, em um idioma não oo, geralmente você precisa de um parâmetro extra - todos os idiomas OO têm um parâmetro "oculto" de "this" que, de outra forma, deveria ser passado (ou, em um idioma / design mais horrível, pode ser globalmente acessível)
Bill K
13

Parece que existem outras considerações além do mero número, aqui estão algumas que vêm à mente:

  1. relação lógica com o objetivo principal da função x configurações pontuais

  2. Se forem apenas sinalizadores de ambiente, o agrupamento pode ser muito útil

John Mulder
fonte
12

Um dos epigramas de programação conhecidos de Alan Perlis (recontado no ACM SIGPLAN Notices 17 (9), setembro de 1982) afirma que "Se você tem um procedimento com 10 parâmetros, provavelmente perdeu alguns".

Peter S. Housel
fonte
11

De acordo com Steve McConnell no Code Complete , você deve

Limite o número de parâmetros de uma rotina a cerca de sete

Paul Reiners
fonte
3
/: Número que indica que o factóide é composto: xkcd.com/899
user877329
9

Para mim, quando a lista cruza uma linha no meu IDE, é um parâmetro a mais. Eu quero ver todos os parâmetros em uma linha sem quebrar o contato visual. Mas essa é apenas a minha preferência pessoal.

Aprendendo
fonte
Isso é bom até que você com alguns desenvolvedores tente foo (int a, float b, string c, double d). Melhor tentar evitar trabalhar com eles, eu acho. : D
Rontologist
4
Se alguém deu nomes ridiculamente longos às aulas, eu não deixaria isso influenciar como eu defino ou chamo minhas rotinas.
6125 finnw
9
Posso apresentar-lhe o "retorno de carro"?
3
Quando a lista cruza uma linha no seu IDE, seu monitor é muito pequeno para usá-lo com esse código e você deve comprar claramente um monitor com uma resolução horizontal mais alta. Uma quebra de linha ou uma redução da quantidade de parâmetros são apenas soluções alternativas que não resolvem o problema raiz, pois seu monitor é muito pequeno para essa base de código!
Kaiserludi
9

Eu geralmente concordo com 5, no entanto, se houver uma situação em que eu preciso de mais e for a maneira mais clara de resolver o problema, eu usaria mais.

Inisheer
fonte
8

Sete coisas na memória de curto prazo?

  1. Nome da função
  2. Valor de retorno da função
  3. Objetivo da função
  4. Parâmetro 1
  5. Parâmetro 2
  6. Parâmetro 3
  7. Parâmetro 4
Mike Clark
fonte
Bem. É uma regra de ouro. Quando você está codificando o corpo de uma função, não se importa com seu nome, e o valor de retorno e sua bolsa estão intimamente relacionados.
Auron
7
8. ordem dos parâmetros
Eva
7

Nos Piores 5 Snippets de Código , verifique o segundo, "Isso é um construtor". Possui mais de 37 ⋅ 4 ≈ 150 parâmetros:

Aqui um programador escreveu esse construtor [...] Alguns de vocês podem pensar que sim, é um grande construtor, mas ele usou as ferramentas automáticas de geração de código do eclipse [.] NOO, nesse construtor houve um pequeno bug que eu descobri, o que me fez concluir que este construtor foi escrito à mão. (a propósito, essa é apenas a parte superior do construtor, não está completa).

construtor com mais de 150 parâmetros

medopal
fonte
Isso é tão triste ... Não saber sobre "registros", "estruturas" ou "objetos de valor", que agrupam vários valores e dão a eles um nome comum para que você possa representar hierarquicamente muitos deles de maneira legível, é como andar por aí com sapatos desapertem por anos porque nunca ninguém lhe disse que você poderia mesmo fazer isso 🙈
yeoman
6

Um mais que o necessário. Não pretendo ser simplista, mas há algumas funções que necessariamente precisam de algumas opções. Por exemplo:

void *
mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t offset);

Existem 6 argumentos, e cada um deles é essencial. Além disso, não há um vínculo comum entre eles para justificá-los. Talvez você possa definir "struct mmapargs", mas isso seria pior.

Kirk Strauser
fonte
Bem, prote flagspoderia ter sido reunido se o designer sentisse que 5 era, de alguma forma, um número mágico muito melhor que 6. Um pouco como a maneira que opencombina o modo de leitura / gravação com todos os outros sinalizadores diversos. E talvez você possa se livrar offsetespecificando que a seção mapeada começa na posição atual de busca de filedes. Não sei se existem situações em que você pode mmapuma região que não é capaz de lseekacessar, mas, se não, não é estritamente necessário.
21413 Steve Joplin
... então eu acho que mmapé uma boa ilustração do fato de que alguns designers e usuários preferem uma grande lista longa de parâmetros, enquanto outros preferem seguir algumas etapas preparando um número menor de parâmetros antes de fazer a chamada.
21713 Steve Steveop
1
@Steve: Definir a posição de busca com uma chamada separada antecipadamente teria introduzido uma condição de corrida gratuita. Para algumas APIs (OpenGL), existem tantos parâmetros que afetam uma chamada que você realmente precisa usar o estado, mas normalmente, todas as chamadas devem ficar independentes o máximo possível. Ação à distância é o caminho para o lado sombrio.
Ben Voigt
5

De acordo com as práticas recomendadas do Perl , 3 é aceitável, 4 é demais. É apenas uma orientação, mas em nossa loja é isso que tentamos seguir.

Adam Bellaire
fonte
5

Eu mesmo desenharia o limite para funções públicas em 5 parâmetros.

IMHO, listas longas de parâmetros são aceitáveis ​​apenas em funções auxiliares particulares / locais que devem ser chamadas apenas de alguns locais específicos no código. Nesses casos, talvez você precise repassar muitas informações de estado, mas a legibilidade não é uma grande preocupação, pois apenas você (ou alguém que manterá seu código e deve entender os fundamentos do seu módulo) precisa se preocupar com chamando essa função.

Kip
fonte
Exatamente o motivo pelo qual muitas pessoas nesse ponto fariam tudo o que não é um argumento real, mas o estado transportado, apenas o estado de um objeto na forma de campos (variáveis-membro). Esse objeto seria representado como uma classe. Seria instanciado uma vez ou para cada uso. Às vezes, esse objeto terá apenas um pacote privado ou método público, que delega trabalho para métodos privados com poucos argumentos cada. Alguns argumentos são agora possíveis porque a configuração e estado agora têm um lugar apropriado :)
yeoman
5

Uma questão relacionada que você deve considerar é quão coesa é a rotina. Um grande número de parâmetros pode ser um cheiro que está lhe dizendo que a própria rotina está tentando fazer muito e, portanto, é suspeita de coesão. Concordo que um número rígido e rápido de parâmetros provavelmente é impossível, mas acho que uma rotina de alta coesão implicaria um número baixo de parâmetros.

Onorio Catenacci
fonte
4

Eu paro em três parâmetros como regra geral. Mais e é hora de passar uma matriz de parâmetros ou um objeto de configuração, o que também permite que parâmetros futuros sejam adicionados sem alterar a API.

Eran Galperin
fonte
Se a API for alterada, ela deverá realmente mudar, não apenas ter uma alteração furtiva onde a incompatibilidade ainda possa ocorrer, mas ser menos óbvia.
Wnoise 6/10/08
no entanto, se precisar de mais um parâmetro para configurar um caso extremo, não deve propagar para componentes não relacionados usando a API
Eran Galperin
4

Uma restrição de comprimento em uma lista de parâmetros é apenas mais uma restrição. E restrição significa violência aplicada. Parece engraçado, mas você pode não ser violento mesmo durante a programação. Apenas deixe o código ditar as regras. É óbvio que, se você tiver muitos parâmetros, o corpo do método function / class será grande o suficiente para utilizá-los. E trechos de código grandes geralmente podem ser refatorados e divididos em partes menores. Assim, você obtém uma solução contra ter muitos parâmetros como bônus grátis, pois eles são divididos entre os menores pedaços de código refatorados.

Anônimo
fonte
4

Uma coisa que eu apontaria da perspectiva do desempenho é que, dependendo de como você passa parâmetros para um método, a passagem de muitos parâmetros por valor atrasará o programa, pois cada parâmetro deve ser copiado e, em seguida, colocado na pilha.

Usar uma única classe para abranger todos os parâmetros funcionaria melhor porque um único parâmetro passado por referência seria elegante, limpo e rápido!

Dominic Zukiewicz
fonte
Estou dando a esta resposta um +1 porque é a única que discute qualquer coisa além da limpeza de código ou limites arbitrários, que são subjetivos. Algumas pessoas podem não pensar no que você está fazendo na pilha quando você está em um loop e pressionando dezenas de argumentos dentro e fora da pilha. Se estiver em um loop, considere usar o número de argumentos transmitidos por REGISTERS na ABI que você está compilando. Por exemplo, na ABI do MS x64, o número máximo de argumentos passados ​​pelos registros é 4. A ABI "System V" (usada pelo sistema operacional não Windows) usa mais registros, portanto, usando 4 argumentos bastante portáteis
Lakey
3

Segundo mim, pode haver casos em que você excederá 4 ou algum número fixo. Coisas a serem observadas

  1. Seu método está fazendo muito e você precisa refatorar.
  2. Você pode considerar usar uma coleção ou alguma estrutura de dados.
  3. Repensar o design da sua turma, talvez algumas coisas não precisem ser contornadas.

Do ponto de vista da facilidade de uso ou da facilidade de leitura do código, acho que quando você precisa digitar sua assinatura de método, isso deve fazer com que você pare e pense: a menos que se sinta impotente e todos os esforços para reduzir a assinatura levam a nenhum resultado. Algumas bibliotecas muito boas no passado e no presente usam mais de 4-5 carrinhos de bebê.

Perpetualcoder
fonte
3

Minha regra geral é que preciso lembrar os parâmetros por tempo suficiente para olhar para uma chamada e dizer o que ela faz. Então, se eu não consigo olhar para o método e, em seguida, mudar para a chamada de um método e lembrar qual parâmetro faz o que, então há muitos.

Para mim, isso equivale a cerca de 5, mas eu não sou tão brilhante. Sua milhagem pode variar.

Você pode criar um objeto com propriedades para reter os parâmetros e passá-lo se exceder o limite que você definir. Consulte o livro Refatoração de Martin Fowler e o capítulo sobre como simplificar as chamadas de método.

Mike Two
fonte
1

Depende bastante do ambiente em que você está trabalhando. Tomemos, por exemplo, javascript. Em javascript, a melhor maneira de passar parâmetros é usar objetos com pares de chave / valor, o que, na prática, significa que você tem apenas um parâmetro. Em outros sistemas, o ponto ideal será às três ou quatro.

No final, tudo se resume ao gosto pessoal.

Joeri Sebrechts
fonte
1

Concordo que 3 é bom, 4 é demais como orientação. Com mais de três parâmetros, você está inevitavelmente executando mais de uma tarefa. Mais de uma tarefa deve ser dividida em métodos separados.

No entanto, se eu olhasse para o projeto mais recente em que trabalhei, as exceções seriam muitas e a maioria dos casos seria difícil de reduzir para três parâmetros.

kae
fonte
1

Se eu tiver 7 a 10 parâmetros em uma rotina, analiso agrupá-los em uma nova classe, mas não se essa classe não passasse de um monte de campos com getters e setters - a nova classe precisa fazer algo diferente de valores aleatórios e Fora. Caso contrário, eu prefiro aturar a longa lista de parâmetros.

finnw
fonte
1
Vou agrupá-lo com uma classe somente de dados se for usado em mais de um local, mas mesmo assim eu costumo criar os dois construtores.
Leahn Novash
1

É um fato conhecido que, em média, as pessoas podem manter 7 +/- 2 coisas na cabeça por vez. Eu gosto de usar esse princípio com parâmetros. Supondo que todos os programadores sejam pessoas inteligentes acima da média, eu diria que tudo que é 10+ é demais.

BTW, se os parâmetros forem semelhantes de alguma forma, eu os colocaria em um vetor ou lista em vez de uma estrutura ou classe.

Milan Babuškov
fonte
1

Basearia minha resposta na frequência com que a função é chamada.

Se é uma função init que é chamada apenas uma vez, deixe 10 parms ou mais, quem se importa.

Se for chamado várias vezes por quadro, então eu tendem a fazer uma estrutura e apenas passar um ponteiro para ela, pois isso tende a ser mais rápido (supondo que você não esteja reconstruindo a estrutura todas as vezes).

KPexEA
fonte
1

De acordo com Jeff Bezos, da Amazon, não pode mais do que ser alimentado com duas pizzas :

Kevin Pang
fonte