Las clases estáticas tienen mala prensa en el mundo del desarrollo, pero no dejan de ser una herramienta que puede resultar de utilidad. Es cierto que usar clases y métodos estáticos no da lugar a un código muy “orientado a objetos”, sino más bien todo lo contrario, un código bastante procedural.
Sin embargo, hay casos en los que pueden ayudarnos a escribir un código más legible, y para mi uno de esos casos es el de las fachadas estáticas.
Bienvenidos al mundo real
Antes de que penséis que me he vuelto loco por defender el uso de clases estáticas, vamos a dejar claras un par de cosas:
El estado global es malo. Cuando se enseña programación estructurada, una de las primeras cosas que se aprende es que las variables globales son malas porque pueden ser modificadas desde cualquier parte y cualquier parte puede depender de ellas, lo que hace que el programa sea más complicado de seguir y mantener.
El estado global existe. Por mucho que nos empeñemos, nos guste o no, casi toda aplicación tiene un estado. Podemos negarlo de una forma u otra, escondiendo las variables globales en clases estáticas, singletons, bases de datos o ficheros de configuración, pero en realidad está ahí.
Al hablar de estado, no penséis sólo en datos. En una aplicación orientada a objetos, parte del estado global son las dependencias entre objetos, puesto que de ellas depende el comportamiento de la aplicación en un momento determinado.
Fachadas estáticas
Si muchas clases dependen de algo, básicamente tenemos 2 opciones:
- Inyectar la dependencia, ya sea a través del constructor, de una propiedad o del método que requiere la dependencia.
- Acceder a la dependencia a través de una clase o método estático.
Si echas de menos un singleton, para mi es equivalente al segundo caso.
Muchas veces la mejor forma de enfrentarse a un problema es asumirlo en lugar de tratar de ocultarlo en capas y capas de abstracción.
Uno de los principales inconvenientes de las clases estáticas es que complican los tests porque no podemos sustituirlas por mocks, stubs o fakes durante los tests unitarios. Ya he dicho en alguna ocasión que no siempre es buena idea sustituir las dependencias por test unitarios, pero hay veces que no queda más remedio.
Tampoco es tan complicado. La fachada estática encapsula la dependencia, por lo que podemos cambiar la dependencia encapsulada.
public static class Localize { private static ILocalizer localizer = new NullLocalizer(); public static void Configure(ILocalizer localizer) { Localize.localizer = localizer; } public static string Text(string text) { return localizer.Translate(text); } }
En este caso tenemos una clase estática encargada de traducir cadenas de texto. Esto es algo que haremos en muchos puntos de la aplicación: en el interfaz de usuario, en la generación de informes, en mensajes generados desde el dominio…
Inyectar en cada clase que tiene que traducir algo un ILocalizer
es tedioso y no siempre fácil. Por ejemplo, en las entidades de dominio que son instanciadas por un ORM no es cómodo inyectar dependencias (y tampoco me parece recomendable, pero esa es otra historia).
Al final es mucho más cómodo y simple usar el método estático Localize.Text()
desde cualquier parte que lo necesite y asumir que tendremos que tener la fachada configurada para usar la aplicación. Lo único que tenemos que hacer es configurar el ILocalizer
que queremos usar durante el proceso de arranque de la aplicación.
Es posible que nuestra implementación de ILocalizer
dependa de una base de datos donde se almacenan los textos, o de ficheros planos o de cualquier otra cosa que no queramos usar en los tests. En ese caso, podemos crear la implementación que consideremos oportuna y configurarla para nuestros tests:
Localize.Configure(new TestLocalizer());
No todo son clavos
Las fachadas estáticas son una herramienta más, pero no son la herramienta. No existe nada que sea la herramienta. Abusar de ellas puede convertir el código en completamente procedural, con una rigidez excesiva, y eso tampoco es bueno.
No hay que olvidar además que seguimos hablando de estado global, por lo que seguimos teniendo los problemas asociados a las cosas globales, especialmente la falta de visibilidad. No es fácil saber qué clases dependen del estado global viendo los parámetros de sus constructores y métodos.
Cuando las dependencias globales no son tan globales, sino que se dan en un grupo de clases (lo que algunos llamarían una capa) hay otras técnicas que pueden resultar mejores, como la inyección de dependencias ambientales por propiedades a través de un layer supertype.
No obstante, a veces una fachada estática es una buena salida para encapsular estado o dependencias globales y puede ayudar a simplificar mucho el código.