Geralmente, uso métodos privados para encapsular a funcionalidade que é reutilizada em vários locais da classe. Mas, às vezes, tenho um método público grande que pode ser dividido em etapas menores, cada uma em seu próprio método privado. Isso tornaria o método público mais curto, mas estou preocupado que forçar quem lê o método a pular para diferentes métodos privados prejudicará a legibilidade.
Existe um consenso sobre isso? É melhor ter métodos públicos longos ou dividi-los em pedaços menores, mesmo que cada peça não seja reutilizável?
coding-style
readability
Jordak
fonte
fonte
Respostas:
Não, este não é um estilo ruim. Na verdade, é um estilo muito bom.
Funções privadas não precisam existir simplesmente por causa da reutilização. Esse é certamente um bom motivo para criá-los, mas existe outro: decomposição.
Considere uma função que faz muito. Tem cem linhas e é impossível argumentar.
Se você dividir essa função em partes menores, ela ainda "funcionará" tanto quanto antes, mas em partes menores. Ele chama outras funções que devem ter nomes descritivos. A função principal é quase como um livro: faça A, depois faça B, depois faça C, etc. As funções que ele chama podem ser chamadas apenas em um local, mas agora são menores. Qualquer função específica é necessariamente protegida por sandbox das outras funções: elas têm escopos diferentes.
Quando você decompõe um problema grande em problemas menores, mesmo que esses problemas (funções) menores sejam usados / resolvidos apenas uma vez, você obtém vários benefícios:
Legibilidade. Ninguém pode ler uma função monolítica e entender o que faz completamente. Você pode continuar mentindo para si mesmo ou dividi-lo em pedaços pequenos que fazem sentido.
Localidade de referência. Agora, torna-se impossível declarar e usar uma variável, depois manter-se por aí e usar novamente 100 linhas depois. Essas funções têm escopos diferentes.
Testando. Embora seja necessário testar unicamente os membros públicos de uma classe, pode ser desejável testar também certos membros particulares. Se houver uma seção crítica de uma função longa que possa se beneficiar do teste, é impossível testá-la independentemente, sem extraí-la para uma função separada.
Modularidade. Agora que você possui funções privadas, pode encontrar um ou mais deles que possam ser extraídos em uma classe separada, seja usada apenas aqui ou reutilizável. Até o ponto anterior, é provável que essa classe separada também seja mais fácil de testar, pois precisará de uma interface pública.
A idéia de dividir o código grande em pedaços menores, mais fáceis de entender e testar, é um ponto-chave do livro Clean Code, do tio Bob . No momento em que escrevi essa resposta, o livro tinha nove anos, mas hoje é tão relevante quanto era naquela época.
fonte
Provavelmente é uma ótima ideia!
Eu discordo de dividir longas seqüências lineares de ação em funções separadas, exclusivamente para reduzir o comprimento médio da função na sua base de código:
Agora você adicionou linhas de origem e reduziu consideravelmente a legibilidade total. Especialmente se você estiver passando muitos parâmetros entre cada função para acompanhar o estado. Caramba!
No entanto , se você conseguiu extrair uma ou mais linhas em uma função pura que serve a um propósito único e claro ( mesmo que seja chamado apenas uma vez ), melhorou a legibilidade:
Isso provavelmente não será fácil em situações do mundo real, mas peças de pura funcionalidade muitas vezes podem ser destacadas se você pensar nisso por tempo suficiente.
Você saberá que está no caminho certo quando tiver funções com bons nomes de verbos e quando a função dos pais as chamar e tudo parecer praticamente como um parágrafo da prosa.
Então, quando você voltar semanas mais tarde para adicionar mais funcionalidades e descobrir que pode realmente reutilizar uma dessas funções, então , alegria arrebatadora! Que maravilha radiante e maravilhosa!
fonte
foo = compose(reglorbulate, deglorbFramb, getFrambulation);
;)A resposta é que depende muito da situação. O @Snowman cobre os aspectos positivos de interromper uma grande função pública, mas é importante lembrar que também pode haver efeitos negativos, como você está certo.
Muitas funções privadas com efeitos colaterais podem tornar o código difícil de ler e bastante frágil. Isto é especialmente verdade quando essas funções privadas dependem dos efeitos colaterais uma da outra. Evite ter funções fortemente acopladas.
Abstrações estão vazando . Embora seja bom fingir como uma operação é executada ou como os dados são armazenados não importa, há casos em que ocorre e é importante identificá-los.
Semântica e matéria de contexto. Se você não conseguir capturar claramente o que uma função faz em seu nome, novamente poderá reduzir a legibilidade e aumentar a fragilidade da função pública. Isto é especialmente verdade quando você começa a criar funções privadas com um grande número de parâmetros de entrada e saída. E enquanto a partir da função pública, você vê a que funções privadas ela chama, a partir da função privada, não vê como as funções públicas a chamam. Isso pode levar a uma "correção de bug" em uma função privada que interrompe a função pública.
Código fortemente decomposto é sempre mais claro para o autor do que para outros. Isso não significa que ainda não esteja claro para os outros, mas é fácil dizer que faz todo o sentido que
bar()
deve ser chamado antesfoo()
no momento da redação.Reutilização perigosa. Sua função pública provavelmente restringiu as entradas possíveis para cada função privada. Se essas suposições de entrada não forem capturadas adequadamente (documentar TODAS as suposições é difícil), alguém poderá reutilizar indevidamente uma de suas funções privadas, introduzindo erros na base de código.
Decomponha funções com base em sua coesão e acoplamento internos, e não em comprimento absoluto.
fonte
MainMethod
(fácil de obter, geralmente são incluídos símbolos e você deve ter um arquivo de desreferência de símbolos), que é de 2k linhas (os números de linhas nunca são incluídos em relatórios de bugs) e é um NRE que pode acontecer em 1k dessas linhas, bem, eu serei amaldiçoado se eu estiver investigando isso. Mas se eles me disserem que aconteceuSomeSubMethodA
e são 8 linhas e a exceção foi uma NRE, provavelmente vou encontrar e corrigir isso com bastante facilidade.O IMHO, o valor de retirar blocos de código puramente como um meio de quebrar a complexidade, estaria frequentemente relacionado à diferença de complexidade entre:
Uma descrição completa e precisa em linguagem humana do que o código faz, incluindo como ele lida com casos de canto e
O próprio código.
Se o código for muito mais complicado que a descrição, a substituição do código em linha por uma chamada de função poderá facilitar a compreensão do código ao redor. Por outro lado, alguns conceitos podem ser expressos mais facilmente em uma linguagem de computador do que na linguagem humana. Eu consideraria
w=x+y+z;
, por exemplo, mais legível quew=addThreeNumbersAssumingSumOfFirstTwoDoesntOverflow(x,y,z);
.À medida que uma função grande é dividida, haverá cada vez menos diferença entre a complexidade das sub-funções e suas descrições, e a vantagem de outras subdivisões diminuirá. Se as coisas forem divididas a ponto de as descrições serem mais complicadas que o código, novas divisões tornarão o código pior.
fonte
int average3(int n1, int n2, int n3)
. Dividir a função "média" significaria que alguém que quisesse ver se era adequado aos requisitos teria que procurar em dois lugares.É um desafio de equilíbrio.
Melhor é melhor
O método privado efetivamente fornece um nome para o código contido e, às vezes, uma assinatura significativa (a menos que metade de seus parâmetros sejam dados de rastreamento completamente ad-hoc com interdependências pouco claras e não documentadas).
Dar nomes às construções de código é geralmente bom, desde que os nomes sugiram um contrato significativo para o chamador, e o contrato do método privado corresponda exatamente ao que o nome sugere.
Ao se forçar a pensar em contratos significativos para partes menores do código, o desenvolvedor inicial pode detectar alguns erros e evitá-los sem esforço, mesmo antes de qualquer teste de desenvolvimento. Isso só funciona se o desenvolvedor estiver se esforçando para nomear concisa (som simples, mas preciso) e estiver disposto a adaptar os limites do método privado para que a nomeação concisa seja possível.
A manutenção subsequente também é ajudada, porque nomes adicionais ajudam a tornar o código auto-documentado .
Até ficar muito bom
É possível exagerar na atribuição de nomes a pequenos pedaços de código e acabar com muitos métodos privados pequenos demais? Certo. Um ou mais dos seguintes sintomas indicam que os métodos estão ficando muito pequenos para serem úteis:
XxxInternal
ouDoXxx
, especialmente se não houver um esquema unificado para introduzi-las.LogDiskSpaceConsumptionUnlessNoUpdateNeeded
fonte
Ao contrário do que os outros disseram, eu argumentaria que um método público longo é um cheiro de design que não é retificado pela decomposição em métodos privados.
Se esse for o caso, eu argumentaria que cada passo deve ser seu próprio cidadão de primeira classe, com uma única responsabilidade cada. Em um paradigma orientado a objetos, eu sugeriria a criação de uma interface e uma implementação para cada etapa, de modo que cada uma tenha uma responsabilidade única e facilmente identificável e possa ser nomeada de uma maneira que a responsabilidade seja clara. Isso permite testar o método público grande (anteriormente) grande, bem como cada etapa individual, independentemente uma da outra. Todos devem ser documentados também.
Por que não se decompor em métodos particulares? Aqui estão algumas razões:
Este é um argumento que tive recentemente com um de meus colegas. Ele argumenta que ter todo o comportamento de um módulo no mesmo arquivo / método melhora a legibilidade. Concordo que o código é mais fácil de seguir quando todos juntos, mas o código é menos fácil de raciocinar à medida que a complexidade aumenta. À medida que o sistema cresce, torna-se intratável pensar sobre todo o módulo como um todo. Quando você decompõe a lógica complexa em várias classes, cada uma com uma única responsabilidade, fica muito mais fácil argumentar sobre cada parte.
fonte