Eu sempre me perguntei o que outros programadores pensam sobre a idéia de criar funções estéticas puras.
Digamos que eu tenha uma função que processa um pedaço de dados: Function ProcessBigData
. Digamos que eu precisar de várias etapas do processo, só é válida para esses dados: Step1
, Step2
, Step3
.
A abordagem normal que eu mais vejo no código fonte é escrever comentários da seguinte forma:
Function ProcessBigData:
# Does Step1
Step1..
Step1..
#Does Step2
Step2..
Step2..
O que eu costumo fazer, mas sempre me senti errado devido à falta desse estilo de codificação por colegas é:
Function ProcessBigData:
Function Step1:
Step1..
Step1..
Function Step2:
Step2..
Step2..
Step1() -> Step2()
Estou principalmente preocupado se há alguma desvantagem para esse estilo em Javascript e Python
Existem alternativas que não estou vendo?
javascript
python
coding-style
Slytael
fonte
fonte
Respostas:
Não é tão estranho quanto você imagina. Por exemplo, no Padrão ML, é habitual limitar o escopo das funções auxiliares. Concedido, o SML tem sintaxe para facilitar:
Eu consideraria esse bom estilo, uma vez que 1) pequenas funções facilitam o raciocínio sobre o programa e 2) sinaliza ao leitor que essas funções não são usadas fora desse escopo.
Suponho que seja possível que exista alguma sobrecarga na criação de funções internas sempre que a função externa for chamada (não sei se JS ou Python otimizam isso), mas você sabe o que eles dizem sobre otimização prematura.
fonte
Geralmente é bom fazer isso sempre que possível, mas eu gosto de pensar nesse tipo de trabalho não como "etapas", mas como subtarefas .
Uma subtarefa é uma unidade específica de trabalho que pode ser executada: possui uma responsabilidade específica e entradas e saídas definidas (pense no "S" no SOLID ). Uma subtarefa não precisa ser reutilizável: algumas pessoas tendem a pensar "Eu nunca precisarei chamar isso de outra coisa; por que escrevê-lo como uma função?" mas isso é uma falácia.
Vou tentar também descrever os benefícios e também como isso se aplica a funções aninhadas (fechamentos) versus apenas outra função da classe. De um modo geral, eu recomendaria não usar encerramentos, a menos que você precise especificamente de um (existem muitos usos, mas separar o código em blocos lógicos não é um deles).
Legibilidade.
Mais de 200 linhas de código processual (corpo de uma função) são difíceis de ler. As funções de 2-20 linhas são fáceis de ler. Código é para humanos.
Aninhado ou não, você obtém o benefício da legibilidade, a menos que esteja usando muitas variáveis do escopo pai; nesse caso, pode ser igualmente difícil de ler.
Limitar o escopo da variável
Ter outra função obriga a limitar o escopo da variável e a passar especificamente o que você precisa.
Isso geralmente melhora a estrutura do código, porque se você precisar de algum tipo de variável de estado de uma "etapa" anterior, poderá descobrir que há outra subtarefa que deve ser escrita e executada primeiro para obter esse valor. Ou, em outras palavras, torna mais difícil escrever trechos de código altamente acoplados.
Ter funções aninhadas permite acessar variáveis no escopo pai de dentro da função aninhada (fechamento). Isso pode ser muito útil, mas também pode levar a erros sutis e difíceis de encontrar, pois a execução da função pode não ocorrer da maneira como está escrita. Esse é ainda mais o caso se você estiver modificando variáveis no escopo pai (uma péssima idéia, geralmente).
Testes unitários
Cada subtarefa, implementada uma função (ou mesmo uma classe), é um trecho de código testável e independente. Os benefícios do teste de unidade e TDD estão bem documentados em outros lugares.
O uso de funções / fechamentos aninhados não permite testes de unidade. Para mim, este é um rompimento de acordos e a razão pela qual você deve apenas outra função, a menos que haja uma necessidade específica de fechamento.
Trabalhando em equipe / Design de cima para baixo
As subtarefas podem ser escritas por pessoas diferentes, independentemente, se necessário.
Mesmo você mesmo, pode ser útil ao escrever código para simplesmente chamar alguma subtarefa que ainda não existe, ao criar a funcionalidade principal e se preocupar em realmente implementar a subtarefa somente depois que você souber que obterá os resultados necessários em um maneira significativa. Isso também é chamado de design / programação de cima para baixo.
Reutilização de código
Ok, apesar do que eu disse anteriormente, às vezes, na verdade, acaba havendo uma razão para reutilizar uma subtarefa para outra coisa. Não estou de todo defendendo o isismo "astronauta da arquitetura", mas apenas que, ao escrever código pouco acoplado, você pode acabar se beneficiando mais tarde da reutilização.
Muitas vezes, essa reutilização significa refatoração, o que é perfeitamente esperado, mas refatorar os parâmetros de entrada para uma função autônoma pequena é MUITO mais fácil do que extraí-lo de mais de 200 funções de linha meses após a gravação, o que é realmente o meu ponto aqui.
Se você usar uma função aninhada, reutilizá-la geralmente é uma questão de refatorar para uma função separada, e, novamente, é por isso que eu argumentaria que aninhado não é o caminho a seguir.
fonte