Está modificando um parâmetro de entrada um antipadrão? [fechadas]

54

Estou programando em Java e sempre faço conversores parecidos com isso:

public OtherObject MyObject2OtherObject(MyObject mo){
    ... Do the conversion
    return otherObject;
}

No novo local de trabalho, o padrão é:

public void MyObject2OtherObject(MyObject mo, OtherObject oo){
    ... Do the conversion
}

Para mim, é um pouco malcheiroso, pois me acostumei a não alterar os parâmetros de entrada. Esta alteração de parâmetro de entrada é um antipadrão ou está OK? Tem alguns inconvenientes sérios?

    
por CsBalazsHungary 23.06.2014 / 13:53
fonte

8 respostas

65

Não é um antipadrão, é uma prática ruim.

A diferença entre um antipadrão e uma mera má prática está aqui: definição anti-padrão .

O novo estilo de local de trabalho que você mostra é uma prática ruim , vestigial ou pré-OOP, de acordo com o Código Limpo do Uncle Bob.

Arguments are most naturally interpreted as inputs to a function.

Anything that forces you to check the function signature is equivalent to a double-take. It’s a cognitive break and should be avoided. In the days before object oriented programming it was sometimes necessary to have output arguments. However, much of the need for output arguments disappears in OO languages

    
por 23.06.2014 / 17:55
fonte
14

Citando o famoso livro de Robert C. Martin "Código Limpo":

Output arguments should be avoided

Functions should have a small numbers of arguments

O segundo padrão viola as duas regras, particularmente o "argumento de saída". Nesse sentido, é pior que o primeiro padrão.

    
por 23.06.2014 / 14:03
fonte
14

O segundo idioma pode ser mais rápido, porque o chamador pode reutilizar uma variável em um loop longo, em vez de cada iteração criar uma nova instância.

Eu geralmente não usaria, mas por exemplo. na programação do jogo, tem o seu lugar. Por exemplo, olhe para o monte de operações Vector3f do JavaMonkey permite passar a instância que deve ser modificada e retornou como resultado.

    
por 23.06.2014 / 16:17
fonte
10

Eu não acho que esses sejam dois códigos equivalentes. No primeiro caso, você deve criar o otherObject . Você pode modificar a instância existente no segundo. Ambos têm seus usos. O cheiro de código estaria preferindo um ao outro.

    
por 23.06.2014 / 13:57
fonte
6

Realmente depende da linguagem.

Em Java, a segunda forma pode ser um antipadrão, mas algumas linguagens tratam a passagem de parâmetros de maneira diferente. Em Ada e VHDL, por exemplo, em vez de passar por valor ou por referência, os parâmetros podem ter os modos in , out ou in out .

É um erro modificar um parâmetro in ou ler um parâmetro out , mas as alterações em um parâmetro in out são passadas de volta para o chamador.

Portanto, os dois formulários em Ada (estes também são VHDL legais) são

function MyObject2OtherObject(mo : in MyObject) return OtherObject is
begin
    ... Do the conversion
    return otherObject;
end MyObject2OtherObject;

e

procedure MyObject2OtherObject(mo : in MyObject; oo : out OtherObject) is
begin
    ... Do the conversion
    oo := ... the conversion result;
end MyObject2OtherObject;

Ambos têm seus usos; um procedimento pode retornar vários valores em vários parâmetros Out , enquanto uma função só pode retornar um único resultado. Como o objetivo do segundo parâmetro é claramente indicado na declaração do procedimento, não pode haver objeção a este formulário. Eu costumo preferir a função de legibilidade. mas haverá casos em que o procedimento é melhor, por ex. onde o chamador já criou o objeto.

    
por 24.06.2014 / 00:11
fonte
3

Parece realmente fedorento e, sem ver mais contexto, é impossível dizer com certeza. Pode haver duas razões para isso, embora haja alternativas para ambos.

Primeiro, é uma maneira concisa de implementar a conversão parcial ou deixar o resultado com valores padrão se a conversão falhar. Ou seja, você pode ter isto:

public void ConvertFoo(Foo from, Foo to) {
    if (can't convert) {
        return;
    }
    ...
}

Foo a;
Foo b = DefaultFoo();
ConvertFoo(a, b);
// If conversion fails, b is unchanged

Claro, geralmente isso é tratado usando exceções. No entanto, mesmo que as exceções precisem ser evitadas por qualquer motivo, há uma maneira melhor de fazer isso - o Padrão TryParse sendo uma opção.

Outra razão é que poderia ser por razões puramente de consistência, por exemplo, é parte de uma API pública onde este método é usado para todas as funções de conversão por qualquer motivo (como outras funções de conversão com múltiplas saídas).

O Java não é ótimo em lidar com várias saídas - ele não pode ter parâmetros somente de saída, como alguns idiomas, ou ter vários valores de retorno como outros - mas, mesmo assim, você poderia usar objetos de retorno.

A razão de consistência é bastante fraca, mas infelizmente pode ser a mais comum.

  • Talvez os policiais de estilo em seu local de trabalho (ou em sua base de código) tenham origem não-Java e tenham relutado em mudar.
  • Seu código pode ter sido uma porta de um idioma em que esse estilo é mais idiomático.
  • Sua organização pode precisar manter a consistência da API em diferentes idiomas, e esse era o estilo de menor denominador comum (é estúpido, mas acontece mesmo para o Google ).
  • Ou talvez o estilo tenha feito mais sentido no passado distante e tenha se transformado em sua forma atual (por exemplo, poderia ter sido o padrão TryParse, mas algum predecessor bem intencionado removeu o valor de retorno depois de descobrir que ninguém o verificou) .
por 24.06.2014 / 05:45
fonte
2

A vantagem do segundo padrão é que ele força o chamador a assumir a responsabilidade pelo objeto criado. Não há dúvidas se o método criou ou não o objeto de um pool reutilizável. O chamador sabe que eles são responsáveis pelo tempo de vida e pelo descarte do novo objeto.

As desvantagens deste método são:

  1. Não pode ser usado como um método de fábrica, o chamador deve saber o subtipo exato de OtherObject necessário e construí-lo antecipadamente.
  2. Não pode ser usado com objetos que exigem parâmetros em seu construtor, se esses parâmetros vierem de MyObject . OtherObject deve ser construível sem saber sobre MyObject .

A resposta é baseada na minha experiência em c #, espero que a lógica se traduza em Java.

    
por 23.06.2014 / 14:35
fonte
2

Dada a semântica solta - myObjectToOtherObject poderia igualmente significar que você move alguns dados do primeiro objeto para o segundo ou que você o converte inteiramente de novo, o segundo método parece mais adequado. / p>

No entanto, se o nome do método for Convert (que deveria ser se olharmos para a parte "... fazer a conversão"), eu diria que o segundo método não faria sentido. Você não converte um valor em outro valor que já existe, IMO.

    
por 23.06.2014 / 16:52
fonte