É uma boa prática usar funções apenas para centralizar o código comum?

20

Eu me deparo muito com esse problema. Por exemplo, atualmente eu escrevo uma função de leitura e uma função de gravação, e ambas verificam se bufé um ponteiro NULL e se a modevariável está dentro de certos limites.

Isso é duplicação de código. Isso pode ser resolvido movendo-o para sua própria função. Mas devo? Essa será uma função bastante anêmica (não faz muito), é bem localizada (não é para fins gerais) e não se sustenta por si só (não é possível descobrir para que você precisa, a menos que veja onde está) usava). Outra opção é usar uma macro, mas quero falar sobre funções neste post.

Então, você deve usar uma função para algo assim? Quais são os prós e contras?

EpsilonVector
fonte
2
O material de linha única precisa mais do que ser usado em vários locais para garantir a mudança para uma função separada.
1
Ótima pergunta. Houve tantas vezes que me perguntei o mesmo.
Rdsxy

Respostas:

31

Este é um ótimo uso de funções.

Esta será uma função bastante anêmica (não faz muito) ...

Isso é bom. As funções devem fazer apenas uma coisa.

bastante localizado ...

Em um idioma OO, torne-o privado.

(portanto, não de uso geral)

Se ele lida com mais de um caso, é de uso geral. Além disso, generalizar não é o único uso de funções. Eles realmente existem para (1) poupar você escrevendo o mesmo código mais de uma vez, mas também (2) para dividir o código em partes menores para torná-lo mais legível. Neste caso, está atingindo (1) e (2). No entanto, mesmo que sua função estivesse sendo chamada de apenas um local, ela ainda pode ajudar com (2).

e não se sustenta por si só (não consegue descobrir para que precisa, a menos que veja onde é usado).

Crie um bom nome para ele e ele ficará bem por si só. "ValidateFileParameters" ou algo assim. Agora fica bem por si só.

Kramii Restabelecer Monica
fonte
6
Bem pontos de mercadorias. Eu acrescentaria (3) impedi-lo de procurar um bug duplicado em toda a sua base de código quando você puder consertar em um só lugar. A duplicação de código pode levar a um inferno de manutenção muito rápido.
Deadalnix 17/11
2
@deadalnix: Bom ponto. Enquanto escrevia, percebi que meus pontos eram uma simplificação grosseira. Escrever e depurar com mais facilidade é certamente um benefício de dividir as coisas em funções (assim como a capacidade de testar unidades separadamente).
Kramii Restabelecer Monica
11

Tão totalmente deve ser uma função.

if (isBufferValid(buffer)) {
    // ...
}

Muito mais legível e mais sustentável (se a lógica da verificação mudar, você a altera apenas em um só lugar).

Além disso, coisas como essas são incorporadas facilmente, para que você nem precise se preocupar com as despesas gerais das chamadas de função.

Deixe-me fazer uma pergunta melhor. Como não fazer isso é uma boa prática?

Faça a coisa Certa. :)

Yam Marcovic
fonte
4
Se isBufferValid for simplesmente return buffer != null;, acho que você está prejudicando a legibilidade lá.
Pd #
5
@pdr: Nesse caso simples, apenas prejudica a legibilidade se você tem uma mentalidade de maníaco por controle e realmente quer realmente saber como o código verifica se o buffer é válido. Portanto, é subjetivo nesses casos simples.
Spoike
4
@ pdr Eu não sei como isso prejudica a legibilidade por qualquer padrão. Você está isentando outros desenvolvedores de se importarem com o modo como estão fazendo algo e se concentrarem no que estão fazendo. isBufferValidé definitivamente mais legível (no meu livro) do que buffer != null, porque comunica o objetivo mais claramente. E, novamente, sem mencionar que você também evita a duplicação. O que mais você precisa?
Yam Marcovic
5

Na IMO, vale a pena mover trechos de código para suas próprias funções sempre que isso tornar o código mais legível , independentemente de a função ser muito curta ou ser usada apenas uma vez.

É claro que existem limites ditados pelo senso comum. Você não deseja que o WriteToConsole(text)método seja simples Console.WriteLine(text), por exemplo. Mas errar no lado da legibilidade é uma boa prática.

Konamiman
fonte
2

Geralmente, é uma boa ideia usar funções para remover a duplicação no código.

No entanto , pode ser levado longe demais. Esta é uma chamada de julgamento.

Para dar o exemplo de sua verificação de buffer nulo, eu provavelmente diria que o código a seguir é claro o suficiente e não deve ser extraído em uma função separada, mesmo que o mesmo padrão seja usado em alguns lugares.

if (buf==null) throw new BufferException("Null read buffer!");

Se você incluir a mensagem de erro como parâmetro em uma função de verificação nula genérica e também considerar o código necessário para definir a função, não será um salvamento de LOC líquido para substituí-lo por:

checkForNullBuffer(buf, "Null read buffer!");

Além disso, ter que mergulhar na função para ver o que está fazendo durante a depuração significa que a chamada da função é menos "transparente" para o usuário e, portanto, pode ser considerada menos legível / mantida.

Mikera
fonte
Indiscutivelmente, seu exemplo diz mais sobre a falta de suporte contratual no idioma do que uma necessidade real de duplicação. Mas bons pontos de qualquer maneira.
deadalnix
1
Você meio que perdeu o ponto do jeito que eu vejo. Não tenho que passar por cima ou ter que considerar a lógica toda vez que você depurar é o que estou procurando. Se eu quiser saber como é feito, verifico uma vez. Ter que fazer isso toda vez é simplesmente bobo.
Yam Marcovic
Se a mensagem de erro ("Null read buffer") for repetida, essa duplicação deve ser definitivamente eliminada.
precisa
Bem, é apenas um exemplo, mas presumivelmente você terá uma mensagem diferente em cada site de chamada de função - por exemplo, "Null read buffer" e "Null write buffer", por exemplo. A menos que você quer as mesmas mensagens de log / erro para cada situação que você não pode de-duplicá-lo .......
mikera
-1 Preconditions.checkNotNull é uma boa prática, não é ruim. Não há necessidade de incluir a mensagem de string. google-collections.googlecode.com/svn/trunk/javadoc/com/google/…
ripper234
2

Centralizar o código geralmente é sempre uma boa ideia. Devemos reutilizar o código o máximo possível.

No entanto, é importante observar como fazer isso. Por exemplo, quando você tem um código que compute_prime_number () ou check_if_packet_is_bad (), ele é bom. As chances são de que o próprio algoritmo de funcionalidade evolua e será beneficiado.

No entanto, qualquer pedaço de código que se repita como uma prosa não se qualifica para ser centralizado imediatamente. Isto é mau. Você pode ocultar linhas de código arbitrárias dentro de uma função apenas para ocultar um código, ao longo do tempo, quando várias partes do aplicativo começarem a ser usadas, todas elas deverão permanecer compatíveis com as necessidades de todos os chamados da função.

Aqui estão algumas perguntas que você deve fazer antes de fazer

  1. Tem a função que você está criando seu próprio significado inerente ou é apenas um monte de linhas?

  2. Que outro contexto exigirá o uso das mesmas funções? É provável que você precise generalizar um pouco a API antes de usá-la?

  3. Qual será a expectativa de (diferentes partes dos) aplicativos quando você lançar exceções?

  4. Quais são os cenários para ver que as funções evoluirão?

Você também deve verificar se já existe algo assim. Vi tantas pessoas sempre tendendo a redefinir suas macros MIN, MAX, em vez de procurar o que já existe.

Essencialmente, a pergunta é: "Essa nova função é realmente digna de reutilização ou é apenas uma cópia e colagem ?" Se for o primeiro, é bom ir.

Dipan Mehta
fonte
1
Bem, se o código duplicado não tiver um significado próprio, seu código estará dizendo que é necessário refatorar. Porque os locais onde a duplicação ocorre provavelmente também não têm significado próprio.
deadalnix
1

A duplicação de código deve ser evitada. Sempre que você o antecipar, evite a duplicação de código. Se você não o antecipou, aplique a regra 3: refatorar antes que o mesmo trecho de código seja duplicado 3 vezes, anote a peculiaridade quando duplicada 2 vezes.

O que é uma duplicação de código?

  • Uma rotina repetida em vários locais da base de código. Esse tour deve ser mais complexo do que uma simples chamada de função (caso contrário, você não ganhará nada fatorando).
  • Uma tarefa muito comum, mesmo que trivial (geralmente um teste). Isso melhorará o encapsulamento e a semântica no código.

Considere o exemplo abaixo:

if(user.getPrileges().contains("admin")) {
    // Do something
}

torna-se

if(user.isAdmin()) {
    // Do something
}

Você melhorou o encapsulamento (agora você pode alterar as condições para ser um administrador de forma transparente) e a semântica do código. Se um bug for descoberto na maneira como você verifica se o usuário é um administrador, você não precisa lançar toda a sua base de código e fazer uma correção em qualquer lugar (com o risco de esquecer um e obter uma falha de segurança em seu aplicativo).

deadalnix
fonte
1
Btw Exemplo ilustra Lei de Deméter.
MaR
1

DRY é sobre a simplificação da manipulação de código. Você acabou de abordar um ponto importante sobre esse princípio: não se trata de minimizar o número de tokens no seu código, mas de criar pontos únicos de modificação para códigos semanticamente equivalentes . Parece que suas verificações sempre têm a mesma semântica, portanto, elas devem ser colocadas em uma função no caso de você precisar modificá-las.

l0b0
fonte
0

Se você vir duplicado, deverá encontrar uma maneira de centralizá-lo.

As funções são uma boa maneira (talvez não seja a melhor, mas isso depende do idioma). Mesmo que a função seja anêmica, como você diz, isso não significa que permanecerá assim.

E se você tiver que procurar outra coisa também?

Você encontrará todos os lugares em que precisa adicionar a verificação extra ou apenas alterar uma função?

Thanos Papathanasiou
fonte
... por outro lado, e se houver uma probabilidade muito alta de você nunca adicionar algo a ele. Sua sugestão ainda é o melhor curso de ação nesse caso também?
EpsilonVector
1
@EpsilonVector minha regra geral é que, se eu precisar alterá-lo uma vez, é melhor refatorá-lo. Então, no seu caso, eu o deixaria e se tivesse que mudar, isso se tornaria uma função.
Thanos Papathanasiou
0

É quase sempre bom se as seguintes condições forem atendidas:

  • melhora a legibilidade
  • usado dentro de escopo limitado

Em um escopo maior, você deve ponderar cuidadosamente as trocas entre duplicação e dependência. Exemplos de técnicas de limitação de escopo: ocultar em seções ou módulos particulares sem expô-los a público.

MaR
fonte