C # patrón para manejar las "funciones libres" limpiamente, evitando clases estáticas de estilo de ayuda

7

Hace poco estuve revisando algunas clases estáticas de "bolsas de utilidad" de estilo de ayudante que flotan en algunos de los grandes códigos de código de C # con los que trabajo, básicamente, me gusta el siguiente fragmento de código 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 persistido a través de invocaciones,
  • pequeño, y
  • cada una consumida 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 que las respuestas brinden soluciones más relevantes.

Solo para esta pregunta, me referiré a este tipo de método como GLUM (método de utilidad ligera general). La connotación negativa de "glum" está en parte destinada. Lo siento si esto se presenta como un juego de palabras tonto.

Incluso dejando de lado mi propio escepticismo predeterminado acerca de los GLUM, no me gustan las siguientes cosas sobre esto:

  • Una clase estática se usa únicamente como espacio de nombres.
  • El identificador de clase estática básicamente no tiene sentido.
  • Cuando se agrega un nuevo GLUM, o bien (a) esta clase de "bolsa" se toca sin una buena razón o (b) se crea una nueva clase de "bolsa" (que en sí misma no suele ser un problema; lo que es malo es que las nuevas clases estáticas a menudo simplemente repiten el problema de la no relación, pero con menos métodos).
  • La denominación de nombres es ineludiblemente horrible, no estándar y, por lo general, internamente inconsistente, ya sea Helpers , Utilities , o lo que sea.

¿Qué es razonablemente bueno & ¿Un patrón simple para refactorizar esto, preferiblemente abordando las preocupaciones anteriores y preferiblemente con un toque tan ligero como sea posible?

Probablemente debería enfatizar: Todos los métodos con los que estoy tratando no están relacionados entre sí. Parece que no hay una manera razonable de dividirlos en información más detallada, pero aún así bolsas de método de clase estática de varios miembros.

    
pregunta William 08.10.2017 - 06:04

3 respuestas

3

Simplemente adopte 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, por lo que estas clases también deberían hacerlo. La característica using static de C # 6 hace que la vida un poco más fácil, también.

Los nombres de clase malolientes como Helpers indican que los métodos deberían dividirse en clases estáticas mejor nombradas, si es posible, pero uno de sus supuestos enfatizados es que los métodos con los que está tratando no tienen parentesco , lo que implica dividir a una nueva clase estática por método existente. Probablemente está bien, si

  • hay buenos nombres para las nuevas clases estáticas y
  • juzga que realmente beneficia la capacidad de mantenimiento de su proyecto.

Como ejemplo artificial, Helpers.LoadImage podría convertirse en algo así como FileSystemInteractions.LoadImage .

Aún así, podrías terminar con bolsas de métodos de clase estática. Algunas formas en que esto puede suceder:

  • Tal vez solo algunos (o ninguno) de los métodos originales tengan buenos nombres para sus nuevas clases.
  • Tal vez las nuevas clases evolucionen más adelante para incluir otros métodos relevantes.
  • Tal vez el nombre de la clase original no estaba mal y en realidad estaba bien.

Es importante recordar que estas bolsas de métodos de clase estática no son terriblemente infrecuentes en las bases de código de C # reales. 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 que vale y vale la pena si juzga honestamente que realmente beneficia la capacidad de mantenimiento de su proyecto), podría considerar crear una clase estática en lugar de una clase parcial estática, empleando el nombre de clase estática 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

namespace ConsoleApp1
{
    using static Foo;
    using static Flob;

    class Program
    {
        static void Main(string[] args)
        {
            Bar();
            Baz();
            Wibble();
            Wobble();
        }
    }
}

Foo / Bar.cs

namespace ConsoleApp1
{
    static partial class Foo
    {
        public static void Bar() { }
    }
}

Foo / Baz.cs

namespace ConsoleApp1
{
    static partial class Foo
    {
        public static void Baz() { }
    }
}

Flob / Wibble.cs

namespace ConsoleApp1
{
    static partial class Flob
    {
        public static void Wibble() { }
    }
}

Flob / Wobble.cs

namespace ConsoleApp1
{
    static partial class Flob
    {
        public static void Wobble() { }
    }
}
    
respondido por el William 10.10.2017 - 21:35
17

Creo que tienes dos problemas estrechamente relacionados entre sí.

  • Las clases estáticas contienen funciones lógicamente no relacionadas
  • Los nombres de las clases estáticas no proporcionan información útil sobre significado / contexto de las funciones contenidas.

Estos problemas se pueden solucionar combinando solo métodos lógicamente relacionados en una clase estática y moviendo los otros a su propia clase estática. Según su dominio de problemas, usted y su equipo deben decidir qué criterios utilizarán para separar los métodos en clases estáticas relacionadas.

Preocupaciones y posibles soluciones

La mayoría de los métodos no están relacionados entre sí - problema. Combina los métodos relacionados en una clase estática y mueve los métodos no relacionados a sus propias clases estáticas.

Los métodos no tienen un estado explícito persistente en todas 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 en todas las invocaciones de métodos, pero hay mejores formas de hacerlo, como las máquinas de estado y la palabra clave yield .

Los métodos son pequeños - no es un 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 usa únicamente como un espacio de nombres - no es un problema. Así es como se usan los métodos estáticos. Si este estilo de métodos de llamada le molesta, intente usar métodos de extensión o la declaración using static .

El identificador de clase estático básicamente no tiene significado - problema . No tiene sentido porque los desarrolladores están poniendo métodos no relacionados en ello. Coloque métodos no relacionados en sus propias clases estáticas y déles nombres específicos y comprensibles.

Cuando se agrega un nuevo GLUM, o (a) esta clase de "bolsa" se toca (tristeza de SRP) - no es un problema si sigue la idea de que solo de forma lógica Los métodos relacionados deben estar en una clase estática. SRP no se infringe aquí, porque el principio debe aplicarse a las clases o a los 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. Las clases / clases / funciones estáticas son nuestras ( desarrolladores) herramientas, 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 tienen un nombre comprensible, ubicadas lógicamente en espacios de nombres / carpetas con jerarquía lógica, no es un problema.

La denominación de los nombres es ineludiblemente horrible, no estándar y, por lo general, inconsistente internamente, ya sea que se trate de Ayudantes, Utilidades o cualquier otro problema - . Proporcione mejores nombres que describan comportamiento esperado Mantenga solo los métodos relacionados en una clase, que brindan ayuda para nombrar mejor.

    
respondido por el Fabio 08.10.2017 - 07:09
-5

En general, no debería tener ni requerir ningún "método de utilidad general". En su lógica empresarial. Eche otro vistazo y colóquelos en un lugar adecuado.

Si estás escribiendo una biblioteca de matemáticas o algo así, sugeriría, aunque normalmente los odio, Métodos de extensión.

Puede usar los métodos de extensión para agregar funciones a tipos de datos estándar.

Por ejemplo Digamos que tengo una función general MySort () en lugar de Helpers.MySort o agregando un nuevo ListOfMyThings.MySort puedo agregarlo a IEnumerable

    
respondido por el Ewan 08.10.2017 - 09:31

Lea otras preguntas en las etiquetas