Código Limpo: Funções com poucos parâmetros [fechado]

49

Li os primeiros capítulos do Código Limpo de Robert C. Martin, e me parece muito bom, mas tenho uma dúvida, em uma parte é mencionado que é bom (cognitivamente) que as funções tenham o menor número de parâmetros quanto possível, ele sugere que 3 ou mais parâmetros são demais para uma função (que eu acho muito exagerada e idealista), então comecei a me perguntar ...

As práticas de uso de variáveis ​​globais e a transmissão de muitos argumentos sobre as funções seriam práticas de programação ruins, mas o uso de variáveis ​​globais pode reduzir bastante o número de parâmetros nas funções ...

Então, eu queria ouvir o que você pensa sobre isso, vale a pena usar variáveis ​​globais para reduzir o número de parâmetros das funções ou não? Em que casos seria?

O que eu acho é que depende de vários fatores:

  • Tamanho do código fonte.
  • Número de parâmetros em média das funções.
  • Número de funções.
  • Frequência em que as mesmas variáveis ​​são usadas.

Na minha opinião, se o tamanho do código-fonte for relativamente pequeno (como menos de 600 linhas de código), há muitas funções, as mesmas variáveis ​​são passadas como parâmetros e as funções têm muitos parâmetros, então vale a pena usar variáveis ​​globais, mas eu gostaria de saber...

  • Você compartilha minha opinião?
  • O que você acha de outros casos em que o código fonte é maior etc.?

PS . Eu vi esse post , os títulos são muito parecidos, mas ele não pergunta o que eu quero saber.

OiciTrap
fonte
144
Eu não acho que a alternativa seria global, mas sim consolidar argumentos em objetos. Provavelmente é mais uma sugestão que postLetter(string country, string town, string postcode, string streetAddress, int appartmentNumber, string careOf)é uma versão fedorenta do postLetter(Address address). Continue lendo o livro, espero dizer algo assim.
Nathan Cooper
3
@DocBrown Tomei a pergunta como algo mais parecido com o tio Bob, que diz que não use mais de três parâmetros, para contornar esse problema usando variáveis ​​globais, certo? :-) Acho que o autor provável não sabe que existem maneiras melhores de contornar esse problema - como mencionado nas respostas abaixo.
22817 Bydev
9
Não mais que n parâmetros é uma regra de ouro (para qualquer valor de n), não gravada em um diamante. Não confunda bons conselhos com um mandato. Muitos parâmetros geralmente são um cheiro de código que você tem muito em uma função / método. As pessoas costumavam evitar a divisão em várias funções para evitar a sobrecarga adicional das chamadas extras. Poucas aplicações são mais intensivas em perfomance e um criador de perfil pode informar quando e onde você precisa evitar chamadas extras.
Jared Smith
14
Isso mostra o que há de errado com esse tipo de regra sem julgamento: abre a porta para as 'soluções' sem julgamento. Se uma função possui muitos argumentos, pode indicar um design abaixo do ideal, geralmente não apenas da função, mas do contexto em que está sendo usada. A solução (se necessário) é procurar refatorar o código. Não posso lhe dar uma regra simples, geral e sem julgamento de como fazê-lo, mas isso não significa que "não mais que N argumentos" seja uma boa regra.
Sdenham
4
Se você tiver muitos parâmetros para uma função, as probabilidades são de que alguns deles estejam relacionados e devem ser agrupados em um objeto que se torna um único parâmetro que encapsula várias partes de dados. Há uma terceira opção aqui.

Respostas:

113

Não compartilho sua opinião. Na minha opinião, o uso de variáveis ​​globais é uma prática pior do que mais parâmetros, independentemente das qualidades que você descreveu. Meu raciocínio é que mais parâmetros podem tornar um método mais difícil de entender, mas variáveis ​​globais podem causar muitos problemas para o código, incluindo baixa testabilidade, erros de simultaneidade e acoplamento rígido. Não importa quantos parâmetros uma função tenha, ela não terá os mesmos problemas que as variáveis ​​globais.

... as mesmas variáveis ​​são passadas como parâmetros

Ele pode ser um cheiro design. Se você tiver os mesmos parâmetros passados ​​para a maioria das funções do seu sistema, pode haver uma preocupação transversal que deve ser tratada com a introdução de um novo componente. Não acho que passar a mesma variável para muitas funções seja um bom raciocínio para introduzir variáveis ​​globais.

Em uma edição da sua pergunta, você indicou que a introdução de variáveis ​​globais pode melhorar a legibilidade do código. Discordo. O uso de variáveis ​​globais está oculto no código de implementação, enquanto os parâmetros de função são declarados na assinatura. Idealmente, as funções devem ser puras. Eles devem operar apenas com seus parâmetros e não devem ter efeitos colaterais. Se você tem uma função pura, pode raciocinar sobre a função observando apenas uma função. Se sua função não é pura, você deve considerar o estado de outros componentes, e fica muito mais difícil argumentar.

Samuel
fonte
Ok, é bom ouvir outras opiniões, mas sobre o cheiro do design , estou programando em C problemas que são resolvidos por métodos de inteligência artificial e costumo usar muitas funções que quase sempre usam as mesmas matrizes, matrizes ou variáveis ​​(que Eu passo como parâmetros para as funções), estou dizendo isso porque não sinto que possa colocar essas variáveis ​​em um conceito / coisa comum, como uma estrutura ou união, para que eu não saiba como criar um design melhor, é por isso que acho que o uso de variáveis ​​globais nesse caso pode valer a pena, mas provavelmente estou errado.
OiciTrap
11
Isso não soa como um problema de design. Eu ainda acho que passar parâmetros para as funções é o melhor design. Em um sistema de IA como você está descrevendo, acho útil escrever testes de unidade para testar as partes do sistema, e a maneira mais fácil de fazer isso é com parâmetros.
Samuel
96
Mais parâmetros tornam uma sub-rotina mais difícil de entender, variáveis ​​globais tornam todo o programa, ou seja, todas as sub-rotinas mais difíceis de entender.
Jörg W Mittag
2
Eu mantenho uma base de código em que algumas das funções levam mais de uma dúzia de parâmetros. É um grande desperdício de tempo - toda vez que preciso chamar a função, preciso abrir esse arquivo em outra janela para saber em que ordem os parâmetros precisam ser especificados. Se eu usasse um IDE que me desse algo como o intellisense ou se usasse uma linguagem que tivesse nomeado parâmetros, não seria tão ruim, mas quem pode se lembrar em que ordem estão dezenas de parâmetros para todas essas funções?
Jerry Jeremiah
6
A resposta está correta no que diz, mas parece que os OPs entendem mal as recommentações em "Código Limpo" como garantido. Estou certo de que não há recomendação para substituir parâmetros de função por globais nesse livro.
Doc Brown
68

Você deve evitar variáveis ​​globais como a praga .

Eu não colocaria um limite rígido para o número de argumentos (como 3 ou 4), mas você não quer mantê-los ao mínimo, se possível.

Use structs (ou objetos em C ++) para agrupar variáveis ​​em uma única entidade e passar isso (por referência) para funções. Normalmente, uma função obtém uma estrutura ou objeto (com algumas coisas diferentes) passados ​​para ela, juntamente com alguns outros parâmetros que dizem à função para fazer algo no struct.

Para obter um código modular limpo e com bom cheiro, tente seguir o princípio da responsabilidade única . Faça isso com suas estruturas (ou objetos), as funções e os arquivos de origem. Se você fizer isso, o número natural de parâmetros passados ​​para uma função será óbvio.

Robert Bristow-Johnson
fonte
5
Qual é a principal diferença entre passar dezenas de parâmetros ocultos em uma estrutura e passá-los explicitamente?
Ruslan
21
@Ruslan Coesão.
gillifirca abuzittin
9
E é mais provável que você refatorar a função em funções menores, pois você pode apenas passar um parâmetro para as subfunções em vez de dezenas de parâmetros. E menos risco de misturar os parâmetros se você usar argumentos posicionais.
11137 Hans Olsson
8
Tudo bem, mas você precisa garantir a coerência nos Structs - verifique se os parâmetros agrupados em um Struct estão 'relacionados'. Você pode usar mais de um Struct em algumas circunstâncias.
Andrew Dodds
8
@abuzittingillifirca Você não recebe coesão automaticamente. Se a única justificativa para colocar parâmetros em uma estrutura é passá-los para uma função específica, então a coesão é provavelmente ilusória.
sdenham
55

Estamos falando de carga cognitiva, não de sintaxe. Então a pergunta é ... O que é um parâmetro nesse contexto?

Um parâmetro é um valor que afeta o comportamento da função. Quanto mais parâmetros, mais combinações possíveis de valores você obtém, mais difícil é o raciocínio sobre a função.

Nesse sentido, variáveis ​​globais que a função usa são parâmetros. São parâmetros que não aparecem em sua assinatura, que têm problemas de ordem de construção, controle de acesso e remanência.

A menos que esse parâmetro seja chamado de preocupação transversal , ou seja, algum estado de todo o programa que tudo usa, mas nada altera (por exemplo, um objeto de registro), você não deve substituir os parâmetros de função por variáveis ​​globais. Eles ainda seriam parâmetros, mas mais desagradáveis.

Quentin
fonte
11
Para você um +1, seja um vaso sanitário ou um vaso sanitário, eles têm o mesmo cheiro. Bom trabalho apontando o lobo em pele de cordeiro.
Jared Smith
11
+1 por declarar que muitos parms e vars globais são ruins. Mas eu quero esclarecer uma coisa. Na maioria das linguagens, os parâmetros primitivos são passados ​​por valor por padrão (Java), e em outros você pode transmiti-los por valor explicitamente (PL / SQL). Por outro lado, as primitivas globais são sempre acessadas por referência (por assim dizer). Portanto, os parâmetros, pelo menos dos tipos primitivos, são sempre mais seguros que as variáveis ​​globais. Embora, é claro, ter mais de dois ou três parâmetros seja um cheiro, e ter doze parâmetros seja um cheiro enorme que deve ser refatorado.
Tulains Córdova
4
Absolutamente, uma variável global É um parâmetro oculto.
Bill K
+1 Ao seu ponto, vi simulações do MATLAB que dependem de variáveis ​​globais para passar dados. O resultado foi completamente ilegível porque era muito difícil dizer quais variáveis ​​eram parâmetros para qual função.
Cort Ammon
34

IMHO sua pergunta é baseada em um mal-entendido. Em "Código Limpo", Bob Martin não sugere a substituição de parâmetros de função repetidos por globais, isso seria um péssimo conselho. Ele sugere substituí-los por variáveis-membro privadas da classe da função. E ele também propõe classes pequenas e coesas (geralmente menores que as 600 linhas de código que você mencionou), portanto essas variáveis ​​membros definitivamente não são globais.

Então, quando você tem a opinião em um contexto com menos de 600 linhas "usando variáveis ​​globais valeria a pena" , compartilha perfeitamente a opinião do tio Bob. Obviamente, é discutível se "3 parâmetros no máximo" é o número ideal e se essa regra às vezes leva a muitas variáveis ​​de membro, mesmo em classes pequenas. IMHO isso é uma troca, não existe uma regra rígida e rápida sobre onde traçar a linha.

Doc Brown
fonte
10
Eu nunca entendi por que alguém prefere tornar sua classe com status inserindo parâmetros em um construtor em vez de apenas viver com um argumento real. Isso sempre me pareceu um enorme aumento de complexidade. (Um exemplo da vida real que eu vi disso é um objeto de conexão com o banco de dados, que impossibilitava a tentativa de rastrear o estado do banco de dados através do programa quando combinado com a injeção de dependência.) Mas talvez o Código Limpo tenha mais a dizer sobre esse particular sujeito. Globais são, é claro, uma opção ainda pior no que diz respeito a tornar as coisas com estado.
precisa saber é
2
@ jpmc26: só se obtém um "aumento enorme de complexidade" se as classes se tornarem muito grandes e tiverem muitas variáveis ​​de membro, para que o resultado não seja mais coeso.
Doc Brown
4
Penso que essa resposta ignora a dificuldade em gerenciar o estado (que pode até ser mutável) que se espalha por muitas classes. Quando uma função depende não apenas dos argumentos, mas também de como o objeto foi construído (ou mesmo modificado ao longo de sua vida útil), é mais difícil entender seu estado atual quando você faz uma chamada específica. Agora você precisa rastrear a construção de outro objeto, apenas para descobrir o que a chamada fará. Adicionar variáveis ​​de instância aumenta ativamente a quantidade de estado do programa, a menos que você construa o objeto e o jogue fora imediatamente?
jpmc26
3
@ jpmc26 Um padrão que uso regularmente ao refatorar é que o construtor age como definindo um contexto, então os argumentos do método são específicos para uma ação. O objeto não é exatamente com estado, porque o estado que ele mantém nunca muda, mas mover essas ações comuns para os métodos desse contêiner melhora significativamente a legibilidade onde é usado (o contexto é definido apenas uma vez, semelhante aos gerenciadores de contexto do python) e reduz a duplicação se várias chamadas são feitas para os métodos do objeto.
22717 Izkata
3
+1 Por dizer claramente que as variáveis ​​membro não são variáveis ​​globais. Muitas pessoas pensam que são.
Tulains Córdova
34

Ter muitos parâmetros é considerado indesejável, mas transformá-los em campos ou variáveis ​​globais é muito pior, porque não resolve o problema real, mas introduz novos problemas.

Ter muitos parâmetros não é, por si só, o problema, mas é um sintoma de que você pode ter um problema. Considere este método:

Graphics.PaintRectangle(left, top, length, height, red, green, blue, transparency);

Ter 7 parâmetros é um sinal de aviso definitivo. O problema subjacente é que esses parâmetros não são independentes, mas pertencem a grupos. lefte toppertencem juntos como uma Positionestrutura, lengthe heightcomo uma Sizeestrutura, e red, bluee greencomo uma Colorestrutura. E talvez a Colortransparência pertença a uma Brushestrutura? Talvez Positione Sizepertença a uma Rectangleestrutura; nesse caso, podemos considerar transformá-la em um Paintmétodo no Rectangleobjeto? Então, podemos acabar com:

Rectangle.Paint(brush);

Missão cumprida! Mas o importante é que realmente melhoramos o design geral, e a redução no número de parâmetros é uma consequência disso. Se apenas reduzirmos o número de parâmetros sem abordar os problemas subjacentes, poderemos fazer algo assim:

Graphics.left = something;
Graphics.top = something;
Graphics.length = something;
...etc
Graphics.PaintRectangle();

Aqui temos conseguido a mesma redução no número de parâmetro, mas temos realmente fez o design pior .

Conclusão: para qualquer conselho de programação e regras práticas, é realmente importante entender o raciocínio subjacente.

JacquesB
fonte
4
+1 resposta agradável, construtiva, compreensível e não teórica.
AnoE
11
Para não mencionar, o que diabos é o seu "quadrado" fazendo com ambos um comprimento e um atributo de altura. :)
Caractere curinga
11
+1 por reconhecer que a terceira via óbvia implícita em algumas respostas (ou seja, muitas atribuições a alguma estrutura / objeto antes da chamada da função) não é melhor.
benxyzzy
@Wildcard: Obrigado, alterou para "retângulo" para evitar confundir o problema!
JacquesB
7

vale a pena usar variáveis ​​globais para reduzir o número de parâmetros das funções ou não?

Não

Eu li os primeiros capítulos deste livro

Você leu o resto do livro?

Um global é apenas um parâmetro oculto. Eles causam uma dor diferente. Mas ainda é dor. Pare de pensar em maneiras de contornar essa regra. Pense em como segui-lo.

O que é um parâmetro?

São coisas. Em uma caixa bem rotulada. Por que importa quantas caixas você tem quando pode colocar qualquer coisa nela?

É o custo de envio e manuseio.

Move(1, 2, 3, 4)

Diga-me que você pode ler isso. Vá em frente, tente.

Move(PointA, PointB)

É por isso.

Esse truque é chamado de objeto de parâmetro de introdução .

E sim, é apenas um truque se tudo o que você está fazendo é contar parâmetros. O que você deve contar são IDEIAS! Abstrações! Quanto você está me fazendo pensar ao mesmo tempo? Mantenha simples.

Agora esta é a mesma contagem:

Move(xyx, y)

OW! Isso é horrível! O que deu errado aqui?

Não basta limitar o número de idéias. Eles devem ser idéias claras. O que diabos é um xyx?

Agora isso ainda está fraco. Qual é a maneira mais poderosa de pensar sobre isso?

As funções devem ser pequenas. Não é menor que isso.

Tio Bob

Move(PointB)

Por que fazer com que a função faça mais do que realmente precisa? O princípio da responsabilidade única não é apenas para as aulas. Sério, vale a pena mudar uma arquitetura inteira apenas para impedir que uma função se transforme em um pesadelo sobrecarregado com 10 parâmetros às vezes relacionados, alguns dos quais não podem ser usados ​​com outros.

Eu vi código em que o número mais comum de linhas em uma função era 1. Sério. Não estou dizendo que você deve escrever dessa maneira, mas, sheesh, não me diga que um global é a única maneira de você cumprir essa regra. Pare de tentar refatorar adequadamente essa função. Você sabe que pode. Pode quebrar em algumas funções. Na verdade, pode se transformar em alguns objetos. Inferno, você pode até dividir parte disso em uma aplicação totalmente diferente.

O livro não está dizendo para você contar seus parâmetros. Está dizendo para você prestar atenção à dor que está causando. Qualquer coisa que conserte a dor conserta o problema. Apenas esteja ciente quando estiver trocando uma dor por outra.

candied_orange
fonte
3
"Você leu o resto do livro?" Sua pergunta é respondida nas primeiras 8 palavras do meu post ...
OiciTrap
Concordo plenamente - o ponto é decompor o problema em pedaços pequenos. Os objetos podem realmente ajudar a organizar essas peças e agrupá-las; também permitem que uma única unidade conceitual seja passada como um parâmetro, em vez de um monte de peças não conectadas. Fico nervoso quando vejo um método com mais de 3 parâmetros. 5 é uma indicação de que meu design foi péssimo e preciso de outra rodada de refatoração. A melhor maneira que encontrei para resolver problemas de design, como a contagem de parâmetros, é simplesmente refatorar as coisas em unidades menores e mais simples (classes / métodos).
Bill K
2
O tom desta resposta pode ser melhorado. Parece muito abrupto e duro. Por exemplo: "Diga-me que você pode ler isso. Continue, tente". parece muito agressivo e pode ser reescrito como "Das duas chamadas de função acima / abaixo, qual é mais fácil de ler?" Você deve tentar entender o assunto sem agressão. O OP está apenas tentando aprender.
Kyle A
Não esqueça que o OP também está tentando ficar acordado.
candied_orange
4

Eu nunca usaria variáveis ​​globais para reduzir parâmetros. O motivo é que as variáveis ​​globais podem ser alteradas por qualquer função / comando, tornando a entrada da função não confiável e propensa a valores que estão fora do escopo do que a função pode manipular. E se a variável fosse alterada durante a execução da função e metade da função tivesse valores diferentes da outra metade?

A passagem de parâmetros, por outro lado, restringe o escopo da variável a apenas sua própria função, de modo que somente a função possa modificar um parâmetro após a chamada.

Se você precisar passar variáveis ​​globais em vez de um parâmetro, é preferível redesenhar o código.

Apenas meus dois centavos.

Dimos
fonte
"Eu nunca usaria variáveis ​​globais para reduzir parâmetros" concordo totalmente. Cria acoplamentos desnecessários.
22817 bydtev
2

Estou com o tio Bob nisso e concordo que mais de 3 parâmetros são algo a ser evitado (raramente uso mais de 3 parâmetros em uma função). Ter muitos parâmetros em uma única função cria um problema de manutenção maior e provavelmente é um cheiro que sua função está fazendo demais / tem muitas responsabilidades.

Se você estiver usando mais de 3 em um método em uma linguagem OO, deverá considerar que os parâmetros não estão relacionados entre si de alguma forma e, portanto, deveria realmente estar passando um objeto?

Além disso, se você criar mais funções (menores), também notará que as funções tendem a ter mais frequentemente 3 parâmetros ou menos. Extrair função / método é seu amigo :-).

Não use variáveis ​​globais como uma maneira de evitar mais parâmetros! Isso é trocar uma prática ruim por uma prática ainda pior!

bytedev
fonte
1

Uma alternativa válida para muitos parâmetros de função é introduzir um objeto de parâmetro . Isso é útil se você tiver um método composto que transmita (quase) todos os seus parâmetros para vários outros métodos.

Em casos simples, este é um DTO simples que não possui nada além dos parâmetros antigos como propriedades.

Timothy Truckle
fonte
1

O uso de variáveis ​​globais sempre parece uma maneira fácil de codificar (especialmente em um programa pequeno), mas isso dificulta a extensão do código.

Sim, você pode reduzir o número de parâmetros em uma função usando uma matriz para vincular os parâmetros em uma entidade.

function <functionname>(var1,var2,var3,var4.....var(n)){}

A função acima será editada e alterada para [using array associativo] -

data=array(var1->var1,
           var2->var2
           var3->var3..
           .....
           ); // data is an associative array

function <functionname>(data)

Concordo com a resposta de robert bristow-johnson : você pode até usar uma estrutura para ligar dados em uma única entidade.

Narender Parmar
fonte
1

Tomando um exemplo do PHP 4, veja a assinatura da função para mktime():

  • int mktime ([ int $hour = date("H") [, int $minute = date("i") [, int $second = date("s") [, int $month = date("n") [, int $day = date("j") [, int $year = date("Y") [, int $is_dst = -1 ]]]]]]] )

Você não acha isso confuso? A função é chamada "make time", mas são necessários parâmetros de dia, mês e ano, além de três parâmetros de tempo. Quão fácil é lembrar em que ordem eles entram? E se você vir mktime(1, 2, 3, 4, 5, 2246);? Você consegue entender isso sem precisar se referir a mais nada? É 2246interpretado como um horário de 24 horas "22:46"? O que os outros parâmetros significam? Seria melhor como um objeto.

Passando para o PHP 5, agora existe um objeto DateTime. Entre seus métodos estão dois chamados setDate()e setTime(). Suas assinaturas são as seguintes:

  • public DateTime setDate ( int $year , int $month , int $day )
  • public DateTime setTime ( int $hour , int $minute [, int $second = 0 ] )

Você ainda precisa se lembrar que a ordem dos parâmetros vai do maior para o menor, mas é uma grande melhoria. Observe que não há um método único que permita definir todos os seis parâmetros de uma só vez. Você precisa fazer duas chamadas separadas para fazer isso.

O que o tio Bob está falando é evitar ter uma estrutura plana. Os parâmetros relacionados devem ser agrupados em um objeto e, se você tiver mais de três parâmetros, é muito provável que tenha um pseudo-objeto, o que lhe dará a oportunidade de criar um objeto apropriado para uma maior separação. Embora o PHP não tenha uma classe Datee um separado Time, você pode considerar que DateTimerealmente contém um Dateobjeto e um Timeobjeto.

Você poderia ter a seguinte estrutura:

<?php
$my_date = new Date;
$my_date->setDay(5);
$my_date->setMonth(4);
$my_date->setYear(2246);

$my_time = new Time;
$my_time->setHour(1);
$my_time->setMinute(2);
$my_time->setSecond(3);

$my_date_time = new DateTime;
$my_date_time->setTime($my_time);
$my_date_time->setDate($my_date);
?>

É obrigatório definir dois ou três parâmetros de cada vez? Se você deseja alterar apenas a hora ou o dia, agora é fácil fazer isso. Sim, o objeto precisa ser validado para garantir que cada parâmetro funcione com os outros, mas esse era o caso antes.

O mais importante é: é mais fácil entender e, portanto, manter? O bloco de código na parte inferior é maior que uma única mktime()função, mas eu argumentaria que é muito mais fácil entender; mesmo um não programador não teria muita dificuldade em descobrir o que faz. O objetivo nem sempre é um código mais curto ou mais inteligente, mas um código mais sustentável.

Ah, e não use globals!

CJ Dennis
fonte
1

Muitas boas respostas aqui ainda não estão abordando o assunto. Por que essas regras práticas? É sobre escopo, é sobre dependências e é sobre modelagem adequada.

Globais para argumentos são piores porque parece que você simplificou, mas na verdade você apenas ocultou a complexidade. Você não o vê mais no protótipo, mas ainda precisa estar ciente dele (o que é difícil, pois não existe mais para você ver) e entender a lógica da função não o ajudará, pois pode haver outra lógica oculta que interfira nas suas costas. Seu escopo foi espalhado por todo o lugar e você introduziu uma dependência de qualquer coisa, porque o que quer agora pode mexer com sua variável. Não é bom.

O principal a manter para uma função é que você pode entender o que ela faz observando o protótipo e a chamada. Portanto, o nome deve ser claro e inequívoco. Mas também, quanto mais argumentos houver, mais difícil será entender o que faz. Amplia o escopo em sua cabeça, muitas coisas acontecem, é por isso que você deseja limitar o número. É importante que tipo de argumentos você esteja lidando, alguns são piores que outros. Um booleano opcional opcional que permite processamento sem distinção entre maiúsculas e minúsculas não torna a função mais difícil de entender; portanto, você não gostaria de fazer um grande negócio a respeito. Como observação lateral, as enumerações são melhores argumentos do que os booleanos porque o significado de uma enumeração é óbvio na chamada.

O problema típico não é que você escreva uma nova função com uma quantidade enorme de argumentos; você começará com apenas alguns quando ainda não perceber o quão complexo é o problema que está solucionando. À medida que seu programa evolui, as listas de argumentos gradualmente tendem a ficar mais longas. Depois que um modelo estiver em sua mente, você deseja mantê-lo, porque é uma referência segura que você conhece. Mas, retrospectivamente, o modelo pode não ser tão bom. Você perdeu um passo ou muito na lógica e falhou em reconhecer uma entidade ou duas. "OK ... eu poderia começar de novo e passar um dia ou dois refatorando meu código ou ... eu poderia adicionar esse argumento para que eu fizesse a coisa certa depois de tudo e terminasse com isso. Por enquanto. Para este cenário Para tirar esse bug do meu prato, para que eu possa mover a nota para ".

Quanto mais frequentemente você optar pela segunda solução, mais cara será a manutenção e mais difícil e pouco atraente o refator se tornará.

Não há solução de placa de caldeira para reduzir o número de argumentos em uma função existente. Agrupá-los em compostos não está realmente facilitando as coisas, é apenas outra maneira de limpar a complexidade debaixo do tapete. Fazer o certo envolve olhar para toda a pilha de chamadas novamente e reconhecer o que está faltando ou que foi feito de maneira errada.

Martin Maat
fonte
0

Várias vezes eu descobri que agrupar muitos parâmetros que foram enviados juntos melhorou as coisas.

  • Obriga você a dar um bom nome a esse agrupamento de conceitos que são usados ​​juntos. Se você usar esses parâmetros juntos, pode ser que eles tenham um relacionamento (ou que você não deva usar juntos).

  • Uma vez com o objeto, costumo descobrir que alguma funcionalidade pode ser movida para esse objeto aparentemente estúpido. Geralmente é fácil testar e com grande coesão e baixo acoplamento, tornando as coisas ainda melhores.

  • Da próxima vez que precisar adicionar um novo parâmetro, tenho um bom lugar para incluí-lo sem alterar a assinatura de muitos métodos.

Portanto, pode não funcionar 100% das vezes, mas pergunte a si mesmo se essa lista de parâmetros deve ser agrupada em um objeto. E por favor. Não use classes não tipadas como Tuple para evitar a criação do objeto. Você está usando programação orientada a objetos; não se preocupe em criar mais objetos, se precisar deles.

Borjab
fonte
0

Com todo o respeito, tenho certeza de que você perdeu completamente o objetivo de ter um pequeno número de parâmetros em uma função.

A idéia é que o cérebro possa armazenar apenas "informações ativas" de uma só vez e, se você tiver n parâmetros em uma função, terá mais n "partes de informações ativas" que precisam estar em seu cérebro de maneira fácil e precisa compreender o que o código está fazendo (Steve McConnell, no Code Complete (um livro muito melhor, IMO), diz algo semelhante sobre 7 variáveis ​​no corpo de um método: raramente alcançamos isso, mas mais e você está perdendo o capacidade manter tudo em mente).

O objetivo de um número baixo de variáveis ​​é ser capaz de manter pequenos os requisitos cognitivos de trabalhar com esse código, para que você possa trabalhar nele (ou lê-lo) com mais eficiência. Uma causa secundária disso é que o código bem fatorado tende a ter cada vez menos parâmetros (por exemplo, código mal fatorado tende a agrupar um monte de coisas em uma bagunça).

Ao passar objetos em vez de valores, talvez você obtenha um nível de abstração para o seu cérebro, porque agora ele precisa perceber que sim, tenho um SearchContext para trabalhar aqui, em vez de pensar nas 15 propriedades que podem estar nesse contexto de pesquisa.

Ao tentar usar variáveis ​​globais, você foi completamente na direção errada. Agora, além de não ter resolvido os problemas de ter muitos parâmetros para uma função, você tomou esse problema e o lançou em um problema muito, muito melhor, que agora você deve ter em mente!

Agora, em vez de trabalhar apenas no nível da função, você também deve considerar o escopo global do seu projeto (caramba, que terrível! Estremeço ...). Você nem tem todas as informações à sua frente na função (porcaria, qual é o nome dessa variável global?) (Espero que isso não tenha sido alterado por outra coisa desde que chamei essa função).

Variáveis ​​com escopo global são uma das piores coisas para se ver em um projeto (grande ou pequeno). Eles denotam uma deficiência no gerenciamento adequado do escopo, que é uma habilidade altamente fundamental para a programação. Eles. Estão. Mal.

Ao tirar os parâmetros de sua função e colocá-los em globais, você se atirou no chão (ou na perna ou no rosto). E Deus não permita que você decida que , ei, eu posso reutilizar esse global para outra coisa ... minha mente treme com o pensamento.

O ideal é manter as coisas fáceis de gerenciar, e variáveis ​​globais NÃO farão isso. Eu diria que elas são uma das piores coisas que você pode fazer para ir na direção oposta.

jleach
fonte
-1

Encontrei uma abordagem altamente eficaz (em JavaScript) para minimizar o atrito entre interfaces: Use uma interface uniforme para todos os módulos , especificamente: funções de parâmetro único.

Quando você precisar de vários parâmetros: Use um único Objeto / Hash ou Matriz.

Tenha paciência comigo, prometo que não estou trollando ...

Antes de você dizer "qual a utilidade de um único parâmetro param?" Ou "Existe uma diferença entre 1 matriz e funções múltiplas arg?"

Bem, sim. Talvez seja visualmente sutil, mas a diferença é múltipla - eu exploro os muitos benefícios aqui

Aparentemente, algumas pessoas acham que 3 é o número certo de argumentos. Alguns pensam que é 2. Bem, isso ainda sugere a pergunta "qual parâmetro entra em arg [0]?" Em vez de escolher a opção que restringe a interface com uma declaração mais rígida.

Acho que defendo uma posição mais radical: não confie em argumentos posicionais. Eu apenas sinto que é frágil e leva a argumentos sobre a posição dos malditos argumentos. Ignore-o e avance para a inevitável luta sobre os nomes de funções e variáveis. 😉

Sério, depois que a nomeação se estabelecer, espero que você acabe com o código da seguinte forma, que é um pouco auto-documentável, não é sensível à posição e permite que futuras alterações de parâmetros sejam tratadas dentro da função:

function sendMessage({toUser, fromUser, subject, body}) { }

// And call the method like so:
sendMessage({toUser: this.parentUser, fromUser: this.currentUser, subject: ‘Example Alert’})

Dan Levy
fonte
6
Fingir argumentos nomeados não passa realmente apenas um argumento.
JDługosz
Por que "fingir" se eu atingi uma meta que me propus? Estou colocando uma prioridade mais alta nos argumentos nomeados. Como os argumentos posicionais não são óbvios para a chamada de código, e com o número de funções nomeadas, um dev deve memorizar, não é útil lembrar quais parâmetros são onde e quais são opcionais. ... Eventualmente, você deseja adicionar um parâmetro necessário após os opcionais, boa sorte documentando e atualizando esse castelo de cartas.
Dan Levy
2
Parâmetros nomeados também são um truque de aprendizado por reforço - os seres humanos atribuem mais significado às coleções de palavras.
Dan Levy