Las clases estáticas están bien siempre que se utilicen en los lugares correctos.
A saber: métodos que son métodos "hoja" (no modifican el estado, simplemente transforman la entrada de alguna manera). Buenos ejemplos de esto son cosas como Path.Combine. Este tipo de cosas son útiles y hacen que la sintaxis sea más tersa.
Los problemas que tengo con la estática son numerosos:
En primer lugar, si tiene clases estáticas, las dependencias están ocultas. Considera lo siguiente:
public static class ResourceLoader
{
public static void Init(string _rootPath) { ... etc. }
public static void GetResource(string _resourceName) { ... etc. }
public static void Quit() { ... etc. }
}
public static class TextureManager
{
private static Dictionary<string, Texture> m_textures;
public static Init(IEnumerable<GraphicsFormat> _formats)
{
m_textures = new Dictionary<string, Texture>();
foreach(var graphicsFormat in _formats)
{
// do something to create loading classes for all
// supported formats or some other contrived example!
}
}
public static Texture GetTexture(string _path)
{
if(m_textures.ContainsKey(_path))
return m_textures[_path];
// How do we know that ResourceLoader is valid at this point?
var texture = ResourceLoader.LoadResource(_path);
m_textures.Add(_path, texture);
return texture;
}
public static Quit() { ... cleanup code }
}
Mirando TextureManager, no puede decir qué pasos de inicialización deben llevarse a cabo mirando un constructor. Debe profundizar en la clase para encontrar sus dependencias e inicializar las cosas en el orden correcto. En este caso, necesita que ResourceLoader se inicialice antes de ejecutarse. Ahora amplíe esta pesadilla de dependencia y probablemente pueda adivinar lo que sucederá. Imagínese tratando de mantener el código donde no hay un orden explícito de inicialización. Compare esto con la inyección de dependencias con instancias; en ese caso, el código ni siquiera se compilará si las dependencias no se cumplen.
Además, si usa estática que modifica el estado, es como un castillo de naipes. Nunca se sabe quién tiene acceso a qué, y el diseño tiende a parecerse a un monstruo espagueti.
Finalmente, e igualmente importante, el uso de estática vincula un programa a una implementación específica. El código estático es la antítesis del diseño para la prueba. Probar código plagado de estática es una pesadilla. Una llamada estática nunca se puede intercambiar por un doble de prueba (a menos que use marcos de prueba diseñados específicamente para simular tipos estáticos), por lo que un sistema estático hace que todo lo que lo usa sea una prueba de integración instantánea.
En resumen, las estáticas están bien para algunas cosas y para herramientas pequeñas o código desechable no desaconsejaría su uso. Sin embargo, más allá de eso, son una maldita pesadilla para el mantenimiento, el buen diseño y la facilidad de prueba.
Aquí hay un buen artículo sobre los problemas: http://gamearchitect.net/2008/09/13/an-anatomy-of-despair-managers-and-contexts/