Eu adorava classes utilitárias preenchidas com métodos estáticos. Eles fizeram uma grande consolidação de métodos auxiliares que, de outra forma, ficariam por aí causando redundância e manutenção no inferno. São muito fáceis de usar, sem instanciação, sem descarte, apenas esquecem. Eu acho que essa foi minha primeira tentativa involuntária de criar uma arquitetura orientada a serviços - muitos serviços sem estado que apenas fizeram seu trabalho e nada mais. À medida que o sistema cresce, os dragões estão chegando.
Polimorfismo
Digamos que temos o método UtilityClass.SomeMethod que felizmente vibra. De repente, precisamos alterar ligeiramente a funcionalidade. A maior parte da funcionalidade é a mesma, mas precisamos alterar algumas partes. Se não fosse um método estático, poderíamos criar uma classe derivada e alterar o conteúdo do método conforme necessário. Como é um método estático, não podemos. Claro, se precisarmos adicionar funcionalidades antes ou depois do método antigo, podemos criar uma nova classe e chamar a antiga dentro dela - mas isso é nojento.
Problemas de interface
Os métodos estáticos não podem ser definidos por meio de interfaces por razões lógicas. E como não podemos substituir métodos estáticos, as classes estáticas são inúteis quando precisamos transmiti-las pela interface. Isso nos torna incapazes de usar classes estáticas como parte de um padrão de estratégia. Podemos corrigir alguns problemas passando delegados em vez de interfaces .
Testes
Isso basicamente anda de mãos dadas com os problemas de interface mencionados acima. Como nossa capacidade de trocar implementações é muito limitada, também teremos problemas para substituir o código de produção pelo código de teste. Novamente, podemos agrupá-los, mas isso exigirá que alteremos grandes partes do nosso código apenas para poder aceitar wrappers em vez dos objetos reais.
Promove blobs
Como os métodos estáticos são geralmente usados como métodos de utilidade e métodos de utilidade geralmente têm finalidades diferentes, acabaremos rapidamente com uma classe grande preenchida com funcionalidade não coerente - idealmente, cada classe deve ter uma única finalidade no sistema . Prefiro ter cinco vezes mais aulas, desde que seus objetivos sejam bem definidos.
Creep do parâmetro
Para começar, esse método estático bonitinho e inocente pode ter um único parâmetro. À medida que a funcionalidade cresce, alguns novos parâmetros são adicionados. Em breve, são adicionados parâmetros opcionais, portanto criamos sobrecargas do método (ou apenas adicionamos valores padrão, nos idiomas que os suportam). Em pouco tempo, temos um método que leva 10 parâmetros. Somente os três primeiros são realmente necessários, os parâmetros 4-7 são opcionais. Mas se o parâmetro 6 for especificado, é necessário preencher também de 7 a 9 ... Se tivéssemos criado uma classe com o único objetivo de fazer o que esse método estático fazia, poderíamos resolver isso incorporando os parâmetros necessários no construtor e permitindo que o usuário defina valores opcionais por meio de propriedades ou métodos para definir vários valores interdependentes ao mesmo tempo. Além disso, se um método cresceu para essa quantidade de complexidade,
Exigindo que os consumidores criem uma instância de classes sem motivo
Um dos argumentos mais comuns é: por que exigir que os consumidores de nossa classe criem uma instância para invocar esse método único, sem ter utilidade para a instância posteriormente? Criar uma instância de uma classe é uma operação muito barata na maioria dos idiomas; portanto, a velocidade não é um problema. Adicionar uma linha de código extra ao consumidor é um custo baixo para estabelecer as bases de uma solução muito mais sustentável no futuro. E, finalmente, se você deseja evitar a criação de instâncias, basta criar um wrapper singleton da sua classe que permita uma reutilização fácil - embora isso exija a exigência de que sua classe não tenha estado. Se não for apátrida, você ainda poderá criar métodos de empacotamento estático que lidam com tudo, enquanto ainda oferece todos os benefícios a longo prazo. Finalmente,
Apenas um Sith lida com absolutos.
É claro que há exceções ao meu desagrado por métodos estáticos. As verdadeiras classes de utilidade que não apresentam nenhum risco de inchaço são excelentes casos para métodos estáticos - System.Convert como exemplo. Se o seu projeto é único, sem requisitos para manutenção futura, a arquitetura geral realmente não é muito importante - estática ou não estática, não importa - a velocidade de desenvolvimento, no entanto.
Padrões, normas, normas!
O uso de métodos de instância não o impede de usar métodos estáticos e vice-versa. Desde que haja um raciocínio por trás da diferenciação e seja padronizado. Não há nada pior do que examinar uma camada de negócios que se estende a diferentes métodos de implementação.