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.
fonte
postLetter(string country, string town, string postcode, string streetAddress, int appartmentNumber, string careOf)
é uma versão fedorenta dopostLetter(Address address)
. Continue lendo o livro, espero dizer algo assim.Respostas:
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.
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.
fonte
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
struct
s (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 nostruct
.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.
fonte
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.
fonte
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.
fonte
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:
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.
left
etop
pertencem juntos como umaPosition
estrutura,length
eheight
como umaSize
estrutura, ered
,blue
egreen
como umaColor
estrutura. E talvez aColor
transparência pertença a umaBrush
estrutura? TalvezPosition
eSize
pertença a umaRectangle
estrutura; nesse caso, podemos considerar transformá-la em umPaint
método noRectangle
objeto? Então, podemos acabar com: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:
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.
fonte
Não
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.
Diga-me que você pode ler isso. Vá em frente, tente.
É 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:
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?
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.
fonte
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.
fonte
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!
fonte
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.
fonte
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.
A função acima será editada e alterada para [using array associativo] -
Concordo com a resposta de robert bristow-johnson : você pode até usar uma estrutura para ligar dados em uma única entidade.
fonte
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? É2246
interpretado 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()
esetTime()
. 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
Date
e um separadoTime
, você pode considerar queDateTime
realmente contém umDate
objeto e umTime
objeto.Você poderia ter a seguinte estrutura:
É 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!
fonte
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.
fonte
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.
fonte
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.
fonte
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:
fonte