Eu ouço muito sobre manter métodos curtos e muitos programadores dizem que o uso de tags #region dentro de um método é um sinal claro de que é muito longo e deve ser refatorado em vários métodos. No entanto, parece-me que há muitos casos em que a separação de código com tags #region dentro de um método é a solução superior à refatoração em vários métodos.
Suponha que tenhamos um método cuja computação possa ser separada em três fases bastante distintas. Além disso, cada um desses estágios é relevante apenas para o cálculo desse método e, portanto, extraí-los em novos métodos não nos permite reutilizar o código. Quais são os benefícios de extrair cada fase em seu próprio método? Pelo que sei, tudo o que ganhamos é alguma legibilidade e um escopo variável separado para cada fase (o que ajudará a impedir que as modificações de uma determinada fase quebrem acidentalmente outra fase).
No entanto, ambos podem ser alcançados sem extrair cada fase em seu próprio método. As tags de região nos permitem recolher o código em um formulário que seja legível (com o benefício adicional de que não precisamos mais deixar nosso lugar neste arquivo, se decidirmos expandir e examinar o código) e simplesmente agrupar cada fase {}
cria seu próprio escopo para trabalhar.
O benefício de fazer dessa maneira é que não poluimos o escopo no nível de classe com três métodos que são realmente relevantes apenas para o funcionamento interno de um quarto método. Refatorar imediatamente um método longo em uma série de métodos curtos me parece ser a reutilização de código equivalente à otimização prematura; você está introduzindo complexidade extra para resolver um problema que, em muitos casos, nunca surge. Você sempre pode extrair uma das fases em seu próprio método posteriormente, se surgir a oportunidade de reutilização de código.
Pensamentos?
fonte
#region
tags, desativo completamente a dobragem de código no Visual Studio. Eu não gosto de código que tenta se esconder de mim.Respostas:
Tudo o que você deve se preocupar é que seu código seja utilizável, não reutilizável. Um macaco pode transformar código utilizável em código reutilizável, se houver alguma transformação a ser feita.
O argumento "só preciso disso aqui" é ruim, para ser educado. A técnica que você está descrevendo é frequentemente chamada de técnica de manchetes e geralmente é mal vista.
Também relevante: os pensamentos de Jeff Atwoods sobre dobragem de código
fonte
Não são as regiões nos métodos que são o problema, mas o caso em que há uma necessidade (ou mesmo um uso) de colocar regiões em métodos.
Tendo depurado muitos métodos de linha de 200 a 300, posso lhe dizer que não gostaria disso em ninguém. Se você está escrevendo e cuidando do seu próprio código, tudo bem - faça o que quiser. Mas uma vez que alguém o veja, deve ficar claro imediatamente o que um método está fazendo.
Não, isso não é bom o suficiente. Divida em regiões o quanto você quiser, mas ainda estou balançando a cabeça só de pensar nisso.
Se você adotar métodos, obtém muitos benefícios:
///
) e digite a descrição, parâmetros, tipo de retorno e tambémexception
,example
,remarks
nós aos detalhes desses cenários. É para onde vai, é para isso!{}
, mas preciso verificar todos os métodos para garantir que você não "trapaceou"? É issododgyObject
que estou usando aqui apenas porque usado nesta região ou veio de outro lugar? Se eu estivesse no meu próprio método, seria capaz de ver facilmente se foi passado para mim ou se eu mesmo o criei (ou melhor, se você o criou).TurnThingsUpsideDown
método falhou - provavelmente está falhando ao transformar uma coisa ruim" é muito melhor do que "Oh, oDoEverything
método falhou - poderia ter sido 50 coisas diferentes e eu vou ter que procurar por uma hora para encontrá-lo" .Tudo isso antes de qualquer reutilização ou improvabilidade. Métodos adequadamente separados obviamente facilitam a reutilização e também permitem que você substitua facilmente um método que não está executando (muito lento, muito buggy, dependência alterada etc.). Você já tentou refatorar algum desses métodos enormes? Não há como saber que o que você está mudando não afetará mais nada.
Encapsule corretamente sua lógica em métodos de tamanho razoável. Eu sei que é fácil para eles ficarem fora de controle e argumentar que não vale a pena redesenhar uma vez nesse momento é outra questão. Mas você deve aceitar o fato de que não há dano e, pelo menos, benefício potencial em ter métodos adequadamente encapsulados, escritos de forma limpa e simplesmente projetados.
fonte
Se o seu método é complexo o suficiente para poder ser separado em três fases distintas, então esses não devem ser apenas três métodos separados ... mas seu método deve ser de fato uma classe separada, dedicada a esse cálculo.
"Mas então minha turma terá apenas um método público!"
Você está certo, e não há nada de errado nisso. A idéia do princípio da responsabilidade única é que sua classe faça uma coisa .
Sua turma pode chamar cada um dos três métodos por vez e retornar o resultado. Uma Coisa.
Como um bônus, agora seu método é testável porque não é mais um método privado.
Mas não importa uma classe; na realidade, você deve ter quatro : um para cada parte do seu método, mais um que tome cada uma das três como uma dependência e as chame na ordem correta, retornando o resultado.
Isso torna suas aulas ainda mais testáveis. Também facilita a troca de uma dessas três peças. Basta escrever uma nova classe herdada da antiga e substituir o comportamento alterado. Ou crie uma interface para o comportamento de cada uma das suas três partes; e, para substituir uma, basta escrever uma nova classe implementando essa interface e substituí-la pela antiga na configuração do contêiner de injeção de dependência.
O que? Você não está usando injeção de dependência? Bem, você provavelmente ainda não viu a necessidade, porque não está seguindo o princípio da responsabilidade única. Depois de iniciar, você descobrirá que a maneira mais fácil de atribuir a cada classe suas dependências é usar um contêiner de DI.
EDIT :
OK, vamos deixar de lado a questão da refatoração. O objetivo
#region
é ocultar o código , o que significa principalmente código que não precisa ser editado em nenhuma circunstância normal. Código gerado é o melhor exemplo. Ele deve estar oculto nas regiões, porque normalmente você não precisa editá-lo. Se você precisar, o ato de expandir uma região fornecerá um aviso no estilo "Here be dragons" para garantir que você saiba o que está fazendo.Portanto, eu diria
#region
que não deve ser usado no meio de um método. Se eu abrir um método, quero ver o código. Usar #region me dá outro nível de ocultação que não preciso, e isso se torna um aborrecimento: desdobro o método ... e ainda não consigo ver nenhum código.Se você estiver preocupado com as pessoas vendo a estrutura do método (e se recusar a refatorá-lo), adicione comentários em banner como este:
Isso ajudará alguém que está navegando no código a ver as partes individuais do seu método sem precisar lidar com as
#region
tags de expansão e recolhimento .fonte
ctrl+n
Eu acho que o exemplo que você dá no seu argumento é menos sobre o YAGNI (você não precisará dele) e mais sobre como escrever um código de qualidade adequado que seja facilmente sustentável.
Nos primeiros cursos de programação, aprendemos que, se um método tem 700 LOC de comprimento, provavelmente é muito grande e certamente seria uma dor gigantesca tentar depurar.
Em segundo lugar, se eu escrever os métodos A, B e C que são usados apenas no método D, posso testar com mais facilidade a unidade AB e C independentemente de D, permitindo testes de unidade mais robustos nos quatro métodos.
fonte
Esses são dois benefícios. Mas o importante, para mim, é a debuggability . Se você precisar rastrear esse código, é muito mais simples poder passar por duas das três seções e rastrear apenas a que mais lhe interessa. A refatoração em métodos menores permite isso; tags de região não.
fonte
ctrl-f10
- corra para o cursorMinha regra pessoal é que, se um método tiver mais de uma tela, digamos 40 linhas, será muito longo. Se uma classe tiver mais de 500 linhas, é muito grande. Eu quebro essas regras algumas vezes, às vezes até por um grande múltiplo, mas geralmente me arrependo dentro de um ano.
Mesmo um método de 20 a 30 linhas está ficando um pouco longo na maioria dos casos. Então, eu diria que usar regiões em um método geralmente é uma má ideia, simplesmente porque quase nunca faria sentido recolher uma região de 20 a 30 linhas em várias sub-regiões.
A divisão de funções que são longas por esta definição tem pelo menos dois benefícios:
Você pode colocar um nome no que essas subfunções estão fazendo, o que esclarece seu pensamento atual e simplifica a tarefa dos mantenedores posteriores.
A decomposição em funções menores obriga a passar apenas os parâmetros necessários para essas funções menores; portanto, o escopo dos dados que eles exigem e modificam é muito claro. Isso pressupõe que você não está acessando e modificando o estado do objeto em centenas de funções folha diferentes, o que eu também desencorajaria.
Na minha experiência, essas regras produzem código que é mais fácil de escrever sem cometer erros comuns e pode ser entendido e modificado posteriormente sem interromper comportamentos sutis. Não importa se a pessoa que precisa entender / modificar o código é outra pessoa ou depois de eu ter dormido e esquecido tudo.
Portanto, consideraria regiões em métodos como um "cheiro de código", indicando que práticas insustentáveis estão sendo aplicadas. Como sempre, pode haver exceções, mas ocorrem com tão pouca frequência que quase não valem a pena discutir.
fonte
Aqui vamos nós de novo ... Existem muitos outros tópicos aqui sobre programadores que acabaram na mesma discussão.
Você aponta alguns argumentos muito interessantes relacionados a funções curtas. Não é uma declaração popular, mas estou com você sobre isso . Depois de discuti-lo muitas vezes antes, tenho a impressão de que a discussão geralmente se resume a se você se concentra principalmente no encapsulamento adequado ou leva ao máximo o desenvolvimento orientado a testes. Esses são os dois principais candidatos ao que parece estar acontecendo ainda mais uma guerra santa, e temo que estejamos do lado fraco.
Contudo ...
A solução na minha opinião definitivamente não está envolvendo as 'fases' nas regiões. A dobragem de código é usada para varrer o código para debaixo do tapete. Deixe o código como está, pois poderá lembrá-lo de que você pode refatorá-lo posteriormente! Para relacioná-lo a um argumento em sua pergunta: como você poderá ver uma oportunidade de reutilização de código se não encontrar nenhum código? Segmentos longos de código devem ser exatamente isso, um espinho no olho que faz com que você queira refatorá-lo.
Como substituto apropriado para regiões, sugiro o uso de parágrafos de código, como Alex Papadimoulis os nomeia em um comentário . Você já deve estar usando uma abordagem semelhante. São simplesmente blocos de código com um cabeçalho de comentário, explicando o que todo o bloco faz. Você tem a legibilidade das funções, mas é capaz de usar frases em inglês normalmente espaçadas e não perde nenhum encapsulamento.
fonte
Embora eu concorde que geralmente é melhor refatorar o código do que usar
#region
, nem sempre é possível.Para apoiar essa afirmação, deixe-me dar um exemplo.
É fácil reajustar as regiões para alterar a ordem ou estender quando campos adicionais são adicionados à classe. Os comentários são necessários de qualquer maneira para ver rapidamente como a ordem deve funcionar. E acima de tudo: não vejo como a refatoração ajudará a legibilidade neste caso.
No entanto, essas exceções são raras. Geralmente é possível refatorar, como muitas das outras respostas dizem.
fonte
Eu não acho que exista uma resposta única para o motivo pelo qual certas pessoas ou equipes decidem não usar,
#region
mas se eu tivesse que listar algumas, essas seriam as principais razões que eu já vi.fonte