Solía amar las clases de utilidad llenas de métodos estáticos. Hicieron una gran consolidación de los métodos auxiliares que de otra manera se encontrarían causando redundancia y un infierno de mantenimiento. Son muy fáciles de usar, no se crean instancias, no se eliminan, solo dispara y olvida. Supongo que este fue mi primer intento involuntario de crear una arquitectura orientada a servicios: muchos servicios sin estado que solo hicieron su trabajo y nada más. Sin embargo, a medida que el sistema crece, vendrán dragones.
Polimorfismo
Digamos que tenemos el método UtilityClass.SomeMethod que felizmente suena. De repente, necesitamos cambiar ligeramente la funcionalidad. La mayor parte de la funcionalidad es la misma, pero debemos cambiar algunas partes. Si no hubiera sido un método estático, podríamos hacer una clase derivada y cambiar el contenido del método según sea necesario. Como es un método estático, no podemos. Claro, si solo necesitamos agregar funcionalidad antes o después del método anterior, podemos crear una nueva clase y llamar a la anterior dentro de ella, pero eso es simplemente asqueroso.
Problemas de interfaz
Los métodos estáticos no se pueden definir a través de interfaces por razones lógicas. Y dado que no podemos anular los métodos estáticos, las clases estáticas son inútiles cuando necesitamos pasarlos por su interfaz. Esto nos hace incapaces de usar clases estáticas como parte de un patrón de estrategia. Podríamos solucionar algunos problemas pasando delegados en lugar de interfaces .
Prueba
Esto básicamente va de la mano con los problemas de interfaz mencionados anteriormente. Como nuestra capacidad de intercambiar implementaciones es muy limitada, también tendremos problemas para reemplazar el código de producción con código de prueba. Nuevamente, podemos envolverlos, pero requerirá que cambiemos grandes partes de nuestro código solo para poder aceptar envoltorios en lugar de los objetos reales.
Fomenta los blobs
Como los métodos estáticos generalmente se usan como métodos de utilidad y los métodos de utilidad generalmente tendrán diferentes propósitos, terminaremos rápidamente con una gran clase llena de funcionalidad no coherente; idealmente, cada clase debería tener un único propósito dentro del sistema . Prefiero tener cinco veces más clases siempre que sus propósitos estén bien definidos.
Parámetro de fluencia
Para empezar, ese pequeño método estático lindo e inocente podría tomar un solo parámetro. A medida que la funcionalidad crece, se agregan un par de parámetros nuevos. Pronto se agregan parámetros adicionales que son opcionales, por lo que creamos sobrecargas del método (o simplemente agregamos valores predeterminados, en idiomas que los admiten). En poco tiempo, tenemos un método que toma 10 parámetros. Solo los primeros tres son realmente necesarios, los parámetros 4-7 son opcionales. Pero si se especifica el parámetro 6, también se deben completar 7-9 ... Si hubiéramos creado una clase con el único propósito de hacer lo que hizo este método estático, podríamos resolver esto tomando los parámetros requeridos en el constructor, y permite al usuario establecer valores opcionales a través de propiedades o métodos para establecer múltiples valores interdependientes al mismo tiempo. Además, si un método ha crecido a esta cantidad de complejidad,
Exigir a los consumidores que creen una instancia de clases sin motivo.
Uno de los argumentos más comunes es, ¿por qué exigir que los consumidores de nuestra clase creen una instancia para invocar este método único, sin tener que usar la instancia después? Crear una instancia de una clase es una operación muy muy barata en la mayoría de los idiomas, por lo que la velocidad no es un problema. Agregar una línea adicional de código al consumidor es un costo bajo para sentar las bases de una solución mucho más fácil de mantener en el futuro. Y, por último, si desea evitar la creación de instancias, simplemente cree un contenedor singleton de su clase que permita una fácil reutilización, aunque esto exige que su clase no tenga estado. Si no es apátrida, aún puede crear métodos de envoltura estática que manejen todo, al tiempo que le brindan todos los beneficios a largo plazo. Finalmente,
Solo un Sith trata en términos absolutos
Por supuesto, hay excepciones a mi disgusto por los métodos estáticos. Las verdaderas clases de utilidad que no presentan ningún riesgo de hinchazón son casos excelentes para los métodos estáticos: System.Convert como ejemplo. Si su proyecto es único, sin requisitos para el mantenimiento futuro, la arquitectura general realmente no es muy importante, estática o no estática, realmente no importa; sin embargo, la velocidad de desarrollo sí lo es.
¡Estándares, estándares, estándares!
El uso de métodos de instancia no le impide usar también métodos estáticos, y viceversa. Mientras haya un razonamiento detrás de la diferenciación y esté estandarizado. No hay nada peor que mirar una capa de negocios en expansión con diferentes métodos de implementación.