Recientemente estuve revisando algunas clases estáticas de "bolsa de utilidad" estilo Helper que flotan alrededor de algunas grandes bases de código C # con las que trabajo, básicamente cosas como el siguiente fragmento muy condensado:
// Helpers.cs
public static class Helpers
{
public static void DoSomething() {}
public static void DoSomethingElse() {}
}
Los métodos específicos que he revisado son
- en su mayoría no relacionados entre sí,
- sin estado explícito persistió a través de invocaciones,
- pequeño y
- cada uno consumido por una variedad de tipos no relacionados.
Editar: lo anterior no pretende ser una lista de supuestos problemas. Es una lista de características comunes de los métodos específicos que estoy revisando. Es un contexto para ayudar a las respuestas a proporcionar soluciones más relevantes.
Solo por esta pregunta, me referiré a este tipo de método como GLUM (método de utilidad general ligero). La connotación negativa de "tristeza" es en parte intencionada. Lo siento si esto aparece como un juego de palabras tonto.
Incluso dejando de lado mi propio escepticismo predeterminado sobre GLUM, no me gustan las siguientes cosas sobre esto:
- Una clase estática se está utilizando únicamente como un espacio de nombres.
- El identificador de clase estática es básicamente sin sentido.
- Cuando se agrega un nuevo GLUM, (a) esta clase de "bolsa" se toca sin razón alguna o (b) se crea una nueva clase de "bolsa" (que en sí misma no suele ser un problema; lo malo es que la nueva las clases estáticas a menudo solo repiten el problema de la falta de relación, pero con menos métodos).
- El meta-denominación es ineludiblemente horrible, no estándar, y por lo general carece de coherencia interna, ya sea
Helpers
,Utilities
o lo que sea.
¿Cuál es un patrón razonablemente bueno y simple para refactorizar esto, preferiblemente abordando las preocupaciones anteriores y preferiblemente con un toque lo más ligero posible?
Probablemente debería enfatizar: todos los métodos con los que estoy tratando no están relacionados por pares entre sí. No parece haber una forma razonable de dividirlos en bolsas de métodos de clase estática de grano fino pero aún con múltiples miembros.
fuente
Respuestas:
Creo que tienen dos problemas estrechamente relacionados entre sí.
Estos problemas se pueden solucionar combinando solo métodos relacionados lógicamente en una clase estática y moviendo los otros a su propia clase estática. Según el dominio del problema, usted y su equipo deben decidir qué criterios utilizará para separar los métodos en clases estáticas relacionadas.
Preocupaciones y posibles soluciones.
Los métodos no están relacionados entre sí - problema. Combine métodos relacionados en una clase estática y mueva métodos no relacionados a sus propias clases estáticas.
Los métodos no tienen un estado explícito persistente en las invocaciones , no es un problema. Los métodos sin estado son una característica, no un error. El estado estático es difícil de probar de todos modos, y solo debe usarse si necesita mantener el estado a través de las invocaciones de métodos, pero hay mejores formas de hacerlo, como máquinas de estado y la
yield
palabra clave.Los métodos son pequeños , no hay problema. - Los métodos deben ser pequeños.
Cada uno de los métodos es consumido por una variedad de tipos no relacionados , no es un problema. Ha creado un método general que puede reutilizarse en muchos lugares no relacionados.
Una clase estática se está utilizando únicamente como un espacio de nombres , no es un problema. Así es como se utilizan los métodos estáticos. Si este estilo de métodos de llamada le molesta, intente usar métodos de extensión o la
using static
declaración.El identificador de clase estático es básicamente sin sentido - problema . No tiene sentido porque los desarrolladores están poniendo métodos no relacionados en él. Coloque métodos no relacionados en sus propias clases estáticas y asígneles nombres específicos y comprensibles.
Cuando se agrega un nuevo GLUM, (a) esta clase de "bolsa" se toca (tristeza SRP) , no es un problema si sigue la idea de que solo los métodos relacionados lógicamente deberían estar en una clase estática.
SRP
no se viola aquí, porque el principio debe aplicarse para clases o métodos. La responsabilidad única de las clases estáticas significa contener diferentes métodos para una "idea" general. Esa idea puede ser una característica o una conversión, o iteraciones (LINQ), o normalización de datos, o validación ...o con menos frecuencia (b) se crea una nueva clase de "bolsa" (más bolsas) , no es un problema. Clases / clases / funciones estáticas: son nuestras herramientas (de desarrolladores) que utilizamos para diseñar software. ¿Su equipo tiene algunos límites para usar las clases? ¿Cuáles son los límites máximos para "más bolsas"? La programación tiene que ver con el contexto, si para la solución en su contexto particular, termina con 200 clases estáticas que, lógicamente, se colocan en espacios de nombres / carpetas con jerarquía lógica, no es un problema.
El meta-denominación es ineludiblemente horrible, no estándar, y por lo general carece de coherencia interna, ya sea ayudantes, Utilidades, o lo que sea - un problema. Proporcione mejores nombres que describan el comportamiento esperado. Mantenga solo los métodos relacionados en una clase, que proporcionan ayuda para una mejor denominación.
fuente
A
con 100 clases estáticas de método único (en 100 archivos) que una clase estáticaA
con 100 métodos en un solo archivo.Math
clase estática.Simplemente abrace la convención de que las clases estáticas se usan como espacios de nombres en situaciones como esta en C #. Los espacios de nombres obtienen buenos nombres, al igual que estas clases. La
using static
característica de C # 6 también hace la vida un poco más fácil.Los nombres de clase malolientes como
Helpers
señal de que los métodos deberían dividirse en clases estáticas mejor nombradas, si es posible, pero una de sus suposiciones enfatizadas es que los métodos con los que está tratando no están relacionados por pares, lo que implica dividirlos en Una nueva clase estática por método existente. Eso probablemente está bien, siComo un ejemplo artificial,
Helpers.LoadImage
podría convertirse en algo asíFileSystemInteractions.LoadImage
.Aún así, podría terminar con bolsas de métodos de clase estática. Algunas formas en que esto puede suceder:
Es importante recordar que estas bolsas de métodos de clase estática no son terriblemente infrecuentes en las bases de código reales de C #. Probablemente no sea patológico tener algunos pequeños .
Si realmente ve una ventaja en tener cada método similar a "función libre" en su propio archivo (lo cual está bien y vale la pena si honestamente juzga que realmente beneficia la mantenibilidad de su proyecto), podría considerar hacer una clase estática en lugar de una estática clase parcial, empleando el nombre de la clase estática de forma similar a como lo haría con un espacio de nombres y luego consumiéndolo a través de
using static
. Por ejemplo:Program.cs
Foo / Bar.cs
Foo / Baz.cs
Flob / Wibble.cs
Flob / Wobble.cs
fuente
En general, no debe tener ni requerir ningún "método de utilidad general". En su lógica de negocios. Eche otro vistazo y colóquelos en un lugar adecuado.
Si estás escribiendo una biblioteca de matemáticas o algo así, te sugiero, aunque normalmente los odio, los métodos de extensión.
Puede usar los métodos de extensión para agregar funciones a los tipos de datos estándar.
Por ejemplo, digamos que tengo una función general MySort () en lugar de Helpers.MySort o agregando una nueva ListOfMyThings.MySort, puedo agregarla a IEnumerable
fuente