Entendo a importância do código bem documentado. Mas também entendo a importância do código de auto-documentação . Quanto mais fácil é ler visualmente uma função específica, mais rápido podemos seguir em frente durante a manutenção do software.
Com isso dito, eu gosto de separar grandes funções em outras menores. Mas faço isso a um ponto em que uma classe pode ter mais de cinco delas apenas para servir a um método público. Agora multiplique cinco métodos privados por cinco públicos, e você terá cerca de vinte e cinco métodos ocultos que provavelmente serão chamados apenas uma vez por esses públicos.
Claro, agora é mais fácil ler esses métodos públicos, mas não posso deixar de pensar que ter muitas funções é uma prática ruim.
[Editar]
As pessoas me perguntam por que acho que ter muitas funções é uma prática ruim.
A resposta simples: é um pressentimento.
Minha crença não é, nem um pouco, apoiada por horas de experiência em engenharia de software. É apenas uma incerteza que me deu um "bloqueio de escritor", mas para um programador.
No passado, eu apenas programava projetos pessoais. Recentemente, mudei para projetos baseados em equipes. Agora, quero garantir que outras pessoas possam ler e entender meu código.
Eu não tinha certeza do que melhoraria a legibilidade. Por um lado, eu estava pensando em separar uma grande função em outras menores com nomes inteligíveis. Mas havia um outro lado de mim dizendo que era apenas redundante.
Então, estou pedindo que isso me ilumine para escolher o caminho correto.
[Editar]
Abaixo, incluí duas versões de como eu poderia resolver o meu problema. O primeiro resolve isso não separando grandes partes do código. O segundo faz coisas separadas.
Primeira versão:
public static int Main()
{
// Displays the menu.
Console.WriteLine("Pick your option");
Console.Writeline("[1] Input and display a polynomial");
Console.WriteLine("[2] Add two polynomials");
Console.WriteLine("[3] Subtract two polynomials");
Console.WriteLine("[4] Differentiate two polynomials");
Console.WriteLine("[0] Quit");
}
Segunda versão:
public static int Main()
{
DisplayMenu();
}
private static void DisplayMenu()
{
Console.WriteLine("Pick your option");
Console.Writeline("[1] Input and display a polynomial");
Console.WriteLine("[2] Add two polynomials");
Console.WriteLine("[3] Subtract two polynomials");
Console.WriteLine("[4] Differentiate two polynomials");
Console.WriteLine("[0] Quit");
}
Nos exemplos acima, o último chama uma função que será usada apenas uma vez durante todo o tempo de execução do programa.
Nota: o código acima é generalizado, mas é da mesma natureza que o meu problema.
Agora, aqui está a minha pergunta: qual? Eu escolho o primeiro ou o segundo?
Respostas:
Isso é conhecido como Encapsulamento , que cria uma Abstração de Controle no nível superior. Isto é uma coisa boa.
Isto significa que quem lê o seu código, quando chegar ao
startTheEngine()
método em seu código, pode ignorar todos os detalhes de nível inferior, comoopenIgnitionModule()
,turnDistributorMotor()
,sendSparksToSparkPlugs()
,injectFuelIntoCylinders()
,activateStarterSolenoid()
, e todos os outros complexos, pequenos, funções que devem ser executados, a fim de facilitar a função muito maior e mais abstrata destartTheEngine()
.A menos que o problema com o qual você esteja lidando no código lide diretamente com um desses componentes, os mantenedores de código podem seguir em frente, ignorando a funcionalidade encapsulada na caixa de areia.
Isso também tem a vantagem de tornar seu código mais fácil de testar. . Por exemplo, eu posso escrever um caso de
turnAlternatorMotor(int revolutionRate)
teste e testar sua funcionalidade completamente independente dos outros sistemas. Se houver um problema com essa função e a saída não for o que eu espero, então eu sei qual é o problema.Código que não é dividido em componentes é muito mais difícil de testar. De repente, seus mantenedores estão apenas olhando para a abstração em vez de poderem se aprofundar em componentes mensuráveis.
Meu conselho é continuar fazendo o que você está fazendo, pois seu código será dimensionado, será fácil de manter e poderá ser usado e atualizado nos próximos anos.
fonte
myString.replaceAll(pattern, originalData);
ou eu colando o método replaceAll inteiro no meu código, incluindo a matriz de caracteres de backup que representa o objeto de string subjacente toda vez que preciso usar a funcionalidade de um String?Sim e não. O princípio fundamental é: um método deve fazer uma coisa e apenas uma coisa
É correto "dividir" seu método em métodos menores. Por outro lado, esses métodos devem ser idealmente gerais o suficiente, não apenas para servir esse método "grande", mas também para serem reutilizáveis em outros lugares. Em alguns casos, um método segue uma estrutura de árvore simples, como um método Initialize que chama InitializePlugins, InitializeInterface etc.
Se você tem um método / classe realmente grande, geralmente é um sinal de que está fazendo muito e é necessário refatorar para interromper o "blob". Talvez oculte alguma complexidade em outra classe sob uma abstração e use injeção de dependência
fonte
Eu acho que, em geral, é melhor errar do lado de muitas funções, em vez de poucas. As duas exceções a essa regra que eu vi na prática são para os princípios DRY e YAGNI . Se você tiver muitas funções quase idênticas, combine-as e use parâmetros para evitar se repetir. Você também pode ter muitas funções se você as criou "apenas por precaução" e elas não estão sendo usadas. Não vejo absolutamente nada de errado em ter uma função que é usada apenas uma vez se adicionar legibilidade e facilidade de manutenção.
fonte
A grande resposta de jmort253 me inspirou a seguir com uma resposta "sim, mas" ...
Ter muitos métodos privados pequenos não é algo ruim, mas preste atenção ao sentimento de insatisfação que está fazendo você postar uma pergunta aqui :). Se você chegar a um ponto em que está escrevendo testes focados apenas em métodos privados (chamando-os diretamente ou configurando um cenário em que um seja chamado de uma certa maneira, apenas para que você possa testar o resultado), então você deve dar um passo atrás.
O que você pode ter é uma nova classe com sua própria interface pública mais focada tentando escapar. Nesse ponto, você pode querer pensar em extrair uma classe para encapsular a parte de sua funcionalidade de classes existente que atualmente vive no método privado. Agora sua classe original pode usar sua nova classe como uma dependência e você pode escrever testes mais focados diretamente nessa classe.
fonte
Quase todos os projetos de OO em que participei sofreram muito com classes muito grandes e métodos muito longos.
Quando sinto a necessidade de um comentário explicando a próxima seção do código, extraio essa seção em um método separado. A longo prazo, isso torna o código mais curto. Esses pequenos métodos são reutilizados mais do que você esperaria inicialmente. Você começa a ver oportunidades de reunir vários deles em uma classe separada. Em breve, você estará pensando em seu problema em um nível superior e todo o esforço será muito mais rápido. Novos recursos são mais fáceis de escrever, porque você pode desenvolver essas pequenas classes criadas a partir desses pequenos métodos.
fonte
Uma classe com 5 métodos públicos e 25 métodos privados não parece tão grande para mim. Apenas certifique-se de que suas aulas tenham responsabilidades bem definidas e não se preocupe muito com o número de métodos.
Dito isso, esses métodos particulares devem se concentrar em um aspecto específico de toda a tarefa, mas não da maneira "doSomethingPart1", "doSomethingPart2". Você não ganha nada se esses métodos particulares são apenas partes de uma longa história arbitrariamente dividida, cada uma sendo sem sentido fora de todo o contexto.
fonte
Trecho do Código Limpo de R. Martin (p. 176):
fonte
Algumas opções:
Isso é bom. Apenas nomeie os métodos privados, assim como seus métodos públicos. E nomeie seus métodos públicos também.
Abstraia alguns desses métodos auxiliares em classes auxiliares ou módulos auxiliares. E certifique-se de que essas classes / módulos auxiliares tenham um bom nome e sejam abstrações sólidas por si só.
fonte
Eu não acho que exista um problema de "muitos métodos privados" se parecer que isso é provavelmente um sintoma da arquitetura de código ruim.
Aqui está como eu penso sobre isso ... Se a arquitetura é bem projetada, geralmente é óbvio onde o código que faz X deve estar. É aceitável se houver algumas exceções a isso - mas elas precisam ser realmente excepcionais, caso contrário refatorem a arquitetura para lidar com elas como padrão.
Se o problema é que, ao abrir sua classe, ele está cheio de métodos particulares que estão dividindo pedaços maiores em pedaços menores de código, talvez isso seja um sintoma de falta de arquitetura. Em vez de classes e arquitetura, essa área de código foi implementada de maneira processual dentro de uma classe.
Encontrar um equilíbrio aqui depende do projeto e de seus requisitos. O contra-argumento para isso é o ditado de que um programador júnior pode interromper a solução em 50 linhas de código que leva um arquiteto a 50 classes.
fonte
Sim.
No Python, o conceito de "privado" (usado pelo C ++, Java e C #) realmente não existe.
Existe uma convenção de nomenclatura "meio que privada", mas é isso.
Privado pode levar a códigos difíceis de testar. Ele também pode quebrar o "Princípio Aberto-Fechado", tornando o código simplesmente fechado.
Consequentemente, para pessoas que usaram Python, funções privadas não têm valor. Apenas torne tudo público e termine com isso.
fonte