Eu tenho um método grande que faz 3 tarefas, cada uma delas pode ser extraída em uma função separada. Se eu criar funções adicionais para cada uma dessas tarefas, ele tornará meu código melhor ou pior e por quê?
Obviamente, ele criará menos linhas de código na função principal, mas haverá declarações de funções adicionais, portanto minha classe terá métodos adicionais, que eu acredito que não são bons, porque tornarão a classe mais complexa.
Devo fazer isso antes de escrever todo o código ou devo deixá-lo até que tudo esteja pronto e depois extrair funções?
java
design-patterns
dhblah
fonte
fonte
Respostas:
Este é um livro com o qual frequentemente ligo, mas aqui vou novamente: Clean Code , de Robert C. Martin , capítulo 3, "Funções".
Você prefere ler uma função com +150 linhas ou uma função que chama 3 +50 linhas? Eu acho que prefiro a segunda opção.
Sim , ele tornará seu código melhor no sentido de que será mais "legível". Crie funções que executem uma e apenas uma coisa; elas serão mais fáceis de manter e produzir um caso de teste.
Além disso, uma coisa muito importante que aprendi com o livro mencionado: escolha nomes bons e precisos para suas funções. Quanto mais importante for a função, mais preciso será o nome. Não se preocupe com o tamanho do nome, se for necessário chamá-lo
FunctionThatDoesThisOneParticularThingOnly
, nomeie-o dessa maneira.Antes de executar seu refator, escreva um ou mais casos de teste. Verifique se eles funcionam. Depois de concluir sua refatoração, você poderá iniciar esses casos de teste para garantir que o novo código funcione corretamente. Você pode escrever testes "menores" adicionais para garantir que suas novas funções funcionem bem separadamente.
Finalmente, e isso não é contrário ao que acabei de escrever, pergunte a si mesmo se você realmente precisa fazer essa refatoração. Confira as respostas para " Quando refatorar ?" (também, pesquise perguntas SO sobre "refatoração", há mais e respostas interessantes para ler)
Se o código já estiver lá e funcionar e você tiver pouco tempo para a próxima versão, não toque nele. Caso contrário, acho que devemos fazer pequenas funções sempre que possível e, como tal, refatorar sempre que houver algum tempo disponível, garantindo que tudo funcione como antes (casos de teste).
fonte
Sim, obviamente. Se é fácil ver e separar as diferentes "tarefas" de função única.
Mas pode haver problemas com isso:
fonte
O problema que você está colocando não é um problema de codificação, convenções ou prática de codificação, mas um problema de legibilidade e de como os editores de texto mostram o código que você escreve. Este mesmo problema está aparecendo também no post:
É correto dividir funções e métodos longos em outros menores, mesmo que eles não sejam chamados por mais nada?
Dividir uma função em subfunções faz sentido ao implementar um grande sistema com a intenção de encapsular as diferentes funcionalidades das quais ele será composto. No entanto, mais cedo ou mais tarde, você se encontrará com várias funções importantes. Alguns deles são ilegíveis e difíceis de manter, se você os mantém como funções longas únicas ou se os dividem em funções menores. Isto é particularmente verdade para as funções em que as operações que você realiza não são necessárias em nenhum outro local do seu sistema. Permite pegar uma de uma função tão longa e considerá-la em uma visão mais ampla.
Pró:
Contra:
Agora vamos imaginar dividir a função longa em várias subfunções e examiná-las com uma perspectiva mais ampla.
Pró:
Contra:
Ambas as soluções têm pró e contra. A melhor solução real seria ter editores que permitissem expandir, em linha e para toda a profundidade, cada chamada de função em seu conteúdo. O que tornaria a divisão de funções em subfunções a única melhor solução.
fonte
Para mim, existem quatro razões para extrair blocos de código em funções:
Você está reutilizando : você acabou de copiar um bloco de código na área de transferência. Em vez de colá-lo, coloque-o em uma função e substitua o bloco por uma chamada de função nos dois lados. Portanto, sempre que você precisar alterar esse bloco de código, precisará alterar apenas essa função em vez de alterar o código em vários locais. Portanto, sempre que você copiar um bloco de código, você deve criar uma função.
É um retorno de chamada : é um manipulador de eventos ou algum tipo de código de usuário que uma biblioteca ou uma estrutura chama. (Mal posso imaginar isso sem criar funções.)
Você acredita que será reutilizado , no projeto atual ou em outro lugar: você acabou de escrever um bloco que calcula a subsequência comum mais longa de duas matrizes. Mesmo que seu programa chame essa função apenas uma vez, eu acreditaria que também precisarei dessa função em outros projetos também.
Você deseja um código de autodocumentação : portanto, em vez de escrever uma linha de comentário em um bloco de código resumindo o que faz, você extrai a coisa toda em uma função e nomeia o que você escreveria em um comentário. Embora eu não seja fã disso, porque gosto de escrever o nome do algoritmo usado, a razão pela qual escolhi esse algoritmo etc. Os nomes das funções seriam muito longos ...
fonte
Tenho certeza de que você ouviu o conselho de que as variáveis devem ter um escopo tão rígido quanto possível e espero que você concorde com isso. Bem, funções são contêineres de escopo e, em funções menores, o escopo das variáveis locais é menor. É muito mais claro como e quando eles devem ser usados e é mais difícil usá-los na ordem errada ou antes de serem inicializados.
Além disso, funções são contêineres de fluxo lógico. Há apenas uma maneira de entrar, as saídas são claramente marcadas e, se a função for curta o suficiente, os fluxos internos deverão ser óbvios. Isso tem o efeito de reduzir a complexidade ciclomática, que é uma maneira confiável de reduzir a taxa de defeitos.
fonte
Além: escrevi isso em resposta à pergunta de dallin (agora fechada), mas ainda acho que poderia ser útil para alguém, então aqui vai
Penso que o motivo das funções de atomização é 2 vezes e, como o @jozefg menciona, depende da linguagem usada.
Separação de preocupações
O principal motivo para fazer isso é manter diferentes partes do código separadas, para que qualquer bloco de código que não contribua diretamente para o resultado / intenção da função seja uma preocupação separada e possa ser extraído.
Digamos que você tenha uma tarefa em segundo plano que também atualiza uma barra de progresso, a atualização da barra de progresso não está diretamente relacionada à tarefa de longa execução, portanto, deve ser extraída, mesmo que seja o único trecho de código que usa a barra de progresso.
Digamos que em JavaScript você tenha uma função getMyData (), que 1) cria uma mensagem de sabão a partir de parâmetros, 2) inicializa uma referência de serviço, 3) chama o serviço com a mensagem de sabão, 4) analisa o resultado, 5) retorna o resultado. Parece razoável, eu escrevi essa função exata várias vezes - mas realmente isso pode ser dividido em 3 funções privadas, incluindo apenas o código 3 e 5 (se houver), pois nenhum outro código é diretamente responsável por obter dados do serviço .
Experiência de depuração aprimorada
Se você possui funções completamente atômicas, seu rastreamento de pilha se torna uma lista de tarefas, listando todo o código executado com êxito, ou seja:
seria muito mais interessante do que descobrir que houve um erro ao obter dados. Porém, algumas ferramentas são ainda mais úteis para depurar árvores de chamadas detalhadas do que, por exemplo, o Microsofts Debugger Canvas .
Também entendo suas preocupações de que pode ser difícil seguir o código escrito dessa maneira, porque no final do dia, você precisa escolher uma ordem de funções em um único arquivo, onde sua árvore de chamadas seria muito mais complexa do que aquela . Mas se as funções tiverem um bom nome (o intellisense me permite usar 3-4 palavras maiúsculas em qualquer função que eu queira, sem abrandar nenhuma) e estruturadas com interface pública na parte superior do arquivo, seu código será como pseudo-código que é de longe a maneira mais fácil de obter um entendimento de alto nível de uma base de código.
Para sua informação, este é um daqueles "faça o que eu digo, não o que faço", manter o código atômico é inútil, a menos que você seja cruelmente consistente com ele no IMHO, o que eu não sou.
fonte