Qual é a melhor prática para ordenar parâmetros em uma função?

45

Às vezes (raramente), parece que criar uma função que tenha uma quantidade decente de parâmetros é a melhor rota. No entanto, quando o faço, sinto que, muitas vezes, escolho a ordenação dos parâmetros aleatoriamente. Eu costumo ir por "ordem de importância", com o parâmetro mais importante primeiro.

Existe uma maneira melhor de fazer isso? Existe uma maneira de "melhor prática" de ordenar parâmetros que aprimore a clareza?

    
por Casey Patton 16.08.2011 / 03:32
fonte

13 respostas

47

Em geral: use .

Escreva um teste para sua função, um teste do mundo real.
Algo que você gostaria de fazer com essa função .

E veja em que ordem você colocou isso abaixo.

A menos que você já tenha (ou saiba) algumas funções que façam algo similar.
Nesse caso: conformar ao que eles já fazem, pelo menos para os primeiros argumentos.

por exemplo. Todos eles pegam um documento / objeto / ponteiro de arquivo / série-de-valores / coordenadas como o (s) primeiro (s) argumento (s)? Pelo amor de Deus, esteja de acordo com esses argumentos .

Evite confundir seus colegas de trabalho e seu futuro.

    
por 16.08.2011 / 03:43
fonte
26

Eu costumo ir com essas regras, embora nem sempre com a mesma precedência. Eu acho que é um processo de pensamento automático agora, e eu não penso demais nisso, exceto pelo design da API pública .

Funil de seleção

  1. Semântica
  2. Importância / Relevância
  3. Frequência de uso
  4. Preocupações de E / S

1. Semântica primeiro

Especialmente em OOP, escolha parâmetros com base no significado semântico da ação ou da mensagem. A assinatura de um método bem-nomeado com um parâmetro bem nomeado deve:

  • parece natural ligar,
  • seja autodescritivo em termos de intenção e comportamento.

(Por esses motivos, às vezes, usar tipos personalizados ou aliases em vez de primitivos pode aumentar a expressividade de sua assinatura.)

2. Então Importância

O parâmetro mais "significativo" vem primeiro (ou próximo ...)

3. Então freqüência

A frequência também é importante, especialmente em um idioma em que você não tem parâmetros nomeados, mas pode ter valores padrão em parâmetros posicionais. Isso implica que a ordem dos parâmetros não varia, e que obviamente você não pode definir os parâmetros N + 1 se quiser forçar o valor padrão do parâmetro Nth (exceto se seu idioma tiver um conceito de um parâmetro de marcador de posição ).

A boa notícia para você é que, geralmente, a frequência está relacionada à importância, de modo que anda de mãos dadas com o ponto anterior. E então é provável que você crie sua API para ter a semântica apropriada.

4. Não vamos esquecer I / O

se o seu método / função receber alguma entrada e produzir uma saída, e a segunda não for "retornada" (por meio de uma instrução de retorno) ou "lançada" (usando um sistema de exceção), você ficará com o opção para passar valores de volta para o chamador usando seus outros parâmetros (ou o parâmetro de entrada). Isso se refere à semântica e, na maioria dos casos, fará sentido que os primeiros parâmetros definam a saída e os últimos parâmetros recebam a saída.

Além disso, uma outra abordagem para ter menos parâmetros e maximizar a semântica seria usar uma abordagem funcional ou definir um padrão do Construtor , para que você possa empilhar claramente suas entradas, definir suas saídas e recuperá-las quando necessário.

(Repare que eu não mencionei variáveis globais, porque por que você usaria um, certo?)

Algumas coisas a serem consideradas

  • Usabilidade

    A maioria dos itens acima será exibida naturalmente se você seguir o aconselhamento da ZJR: Use-o! strong>

  • Considere a refatoração

    Se você se preocupa com a ordem dos parâmetros, talvez essa preocupação encontre sua raiz nos itens acima e no fato de sua API estar mal projetada. Se você tiver muitos parâmetros, algo provavelmente pode ser componentizado / modularizado e refatorado .

  • Considere o desempenho

    Lembre-se de que algumas implementações de idiomas causarão impactos muito importantes no gerenciamento da memória de tempo de execução ao usar parâmetros. Daí a razão pela qual os livros de estilo de muitas línguas recomendam manter a lista de parâmetros simples e curta . Em 4 parâmetros no máximo, por exemplo. Deixo isso como um exercício para você descobrir o porquê.

  • Bevan's responder e mencionar as recomendações do Código Limpo também são definitivamente relevantes!

por 16.08.2011 / 04:12
fonte
19

Eu respeitosamente afirmo que preocupar-se com a ordem dos parâmetros é se preocupar com a coisa errada.

No livro do tio Bob " Código Limpo " ele defende, persuasivamente, que os métodos nunca devem ter mais de dois argumentos - e a maioria deve ter apenas um, se houver. Quando este é o caso, a encomenda é óbvia ou sem importância.

No entanto, de maneira imperfeita, estou tentando seguir o conselho do tio Bob - e isso está melhorando meu código.

Nos casos raros em que um método parece exigir mais informações, introduzindo um objeto de parâmetro é uma boa ideia. Normalmente, acho que este é o primeiro passo para a descoberta de um novo conceito (objeto) que é fundamental para o meu algoritmo.

    
por 16.08.2011 / 05:52
fonte
10

Eu tento colocar os parâmetros IN primeiro, os parâmetros OUT em segundo lugar. Existem também algumas ordenações naturais, por ex. createPoint(double x, double y) é strongmente preferível a createPoint(double y, double x) .

    
por 16.08.2011 / 04:01
fonte
6

Eu nunca vi uma "prática recomendada" documentada em relação a esse tópico específico, mas meu padrão pessoal é listá-los na ordem em que eles aparecerão no método para o qual estão sendo usados ou se o método for mais de um pass-through para uma camada de dados eu vou listá-los na ordem em que eles apareceriam no esquema do banco de dados ou nos métodos da camada de dados.

Além disso, quando há várias sobrecargas de um método, percebo que a maneira típica é listá-las começando com parâmetros que são comuns a todos (ou a maioria) dos métodos, com cada método diferente sendo anexado ao final de cada sobrecarga de método como:

void func1(string param) { }
void func2(string param, int param2) { }
void func3(string param, string param3) { }
void func3(string param, int param2, string param3) { }
    
por 16.08.2011 / 03:42
fonte
6

Ordem: entrada (s), saída (s), parâmetros opcionais.

    
por 16.08.2011 / 05:19
fonte
3

Frequentemente, eu sigo a convenção C / C ++ de colocar os parâmetros const primeiro (ou seja, os parâmetros que você passa por valor), e depois aqueles que você passa por referência. Isto pode não ser necessariamente o método correto de chamar funções, mas, se você estiver interessado em como cada compilador lida com parâmetros, dê uma olhada nos seguintes links para as regras que governam e / ou na ordem em que os parâmetros são colocados na pilha. p>

link

link

    
por 16.08.2011 / 04:11
fonte
1

Eu costumo ir com o pedido de parâmetro "o que parece menos cético". Quanto menos vezes eu precisar ir para a definição do método / função, melhor. E é bom ter os parâmetros nomeados que são descritivos para o que eles são usados, assim quando a pequena dica aparece (VS), então fica ainda mais fácil.

Se você tiver linhas e linhas de parâmetros, convém considerar um design diferente. Dê um passo atrás e veja como você pode dividir isso em mais funções / métodos. Apenas uma ideia, mas quando eu tenho uma dúzia de parâmetros na minha função, quase sempre não é um problema de parâmetro, mas um problema de design.

    
por 16.08.2011 / 05:01
fonte
1

Ordene de qualquer maneira que você acha que o curry provavelmente se beneficiará. Por exemplo, os parâmetros da função primeiro.

    
por 08.03.2012 / 23:14
fonte
1

Sometimes (rarely), it seems that creating a function that takes a decent amount of parameters is the best route.

O uso de vários parâmetros geralmente é um indicador claro de que você viola o SRP nesse método. É improvável que um método que precise de muitos parâmetros faça apenas uma coisa. Excpetion pode ser uma função matemática ou um método de configuração, onde, de fato, vários parâmetros são necessários. Eu evitaria múltiplos parâmetros como o diabo evita a água benta. Quanto mais parâmetros você usar dentro de um método, maior a chance de que o método seja (muito) complexo; mais complexidade significa: mais difícil de manter e menos desejável.

However, when I do, I feel like I'm often choosing the ordering of the parameters at random. I usually go by "order of importance", with the most important parameter first.

No priniple você está escolhendo aleatoriamente . É claro que você pode pensar que o parâmetro A é mais relevante que o parâmetro B ; mas isso pode não ser o caso para os usuários de sua API, que pensam que B é o parâmetro mais relevante. Então, mesmo se você estivesse atento ao escolher o pedido - para outros, poderia parecer aleatório .

Is there a better way to do this? Is there a "best practice" way of ordering parameters that enhances clarity?

Existem várias maneiras de sair:

a) O caso trivial: não use mais de um parâmetro.

b) Como você não especificou qual idioma você escolheu, existe a chance de escolher um idioma com parâmetros nomeados . Isso é um agradável açúcar sintático que permite soltar o significado da ordem dos parâmetros: fn(name:"John Doe", age:36)

Nem toda linguagem permite tais sutilezas. Então o que então?

c) Você poderia usar um Dicionário / Hashmap / Matriz Associativa como parâmetro: por exemplo. O Javascript permitiria o seguinte: fn({"name":"John Doe", age:36}) que não está longe de (b).

d) Claro, se você trabalha com uma linguagem estaticamente tipada como Java. você poderia usar um Hashmap , mas perderia as informações do tipo (por exemplo, ao trabalhar com HashMap<String, Object> ) quando os parâmetros tiverem tipos diferentes (e precisarem transmitir).

A próxima etapa lógica seria passar um Object (se você estiver usando Java) com propriedades apropriadas ou algo mais leve como uma struct (se você escrever, por exemplo, C # ou C / C ++) .

Regra geral:

1) Melhor caso - seu método precisa do parâmetro no em tudo

2) Bom caso - seu método precisa de um parâmetro

3) Caso tolerável - seu método precisa de dois parâmetros

4) Todos os outros casos devem ser refatorados

    
por 09.02.2015 / 21:28
fonte
0

Muitas vezes, um objeto complexo como parâmetro é melhor - uma versão pobre de parâmetros nomeados que funciona na maioria das plataformas. E abre a porta para parâmetros com comportamento para inicializar.

Para um exemplo da maioria das coisas que você não deveria fazer, você pode querer tentar ler a documentação da biblioteca padrão do PHP.

    
por 16.08.2011 / 06:17
fonte
0

Eu geralmente os peço com o requerimento primeiro, do que por alguma medida combinada de importância e frequência de uso de acordo com um "sentimento" (pode ser visto como ORDER BY required DESC, SOME_MAGIC_FEELING(importancy,frequency) ) e não de acordo com qualquer prática específica.

No entanto, como outros já observaram, acho que o problema subjacente que faz disso um problema é usar muitos parâmetros (IMHO, qualquer coisa > 3 é demais) e esse é o verdadeiro problema que você deve abordar. Existe uma postagem interessante sobre isso no blog de rebecca murphey.

Eu acho que quando você tem apenas 1-3 argumentos, a ordenação correta é óbvia e você "sente" o que é certo.

    
por 16.08.2011 / 14:24
fonte
0

Semelhante à resposta da @Wyatt Barnetts, nada além de alguns parâmetros ou parâmetros muito explícitos para o método, eu recomendaria passar um objeto. Normalmente, isso é mais fácil de atualizar / manter, mais claro de ler e elimina a necessidade de se preocupar com o pedido . Além disso, muitos parâmetros para um método são um cheiro de código e há padrões de refatoração que você pode seguir para ajudar a corrigi-lo.

Exemplo explícito:

public int add(int left, int right)
{
  return left + right;
}

Como esse é um exemplo bem definido e a adição é comutativa (a ordem não importa), basta seguir com ela.

No entanto, se você adicionar algo mais complexo:

public SomeComplexReturnValue Calculate(int i, float f, string s, object o)
{
  // do work here
}

se tornaria:

public class SomeComplexInputForCalculation
{
  public int i; 
  public float f;
  public string s; 
  public object o;
}

public SomeComplexReturnValue Calculate(SomeComplexInputForCalculation input)
{
  // do work here
}

Espero que isso ajude ...

    
por 19.08.2011 / 03:16
fonte