Статические классы хороши, если они используются в нужных местах.
А именно: методы, которые являются «листовыми» методами (они не изменяют состояние, они просто каким-то образом преобразуют ввод). Хорошим примером этого являются такие вещи, как Path.Combine. Подобные вещи полезны и позволяют сократить синтаксис.
У меня много проблем со статикой:
Во-первых, если у вас есть статические классы, зависимости скрыты. Учтите следующее:
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 }
}
Глядя на TextureManager, вы не можете сказать, какие шаги инициализации должны быть выполнены, глядя на конструктор. Вы должны углубиться в класс, чтобы найти его зависимости и инициализировать вещи в правильном порядке. В этом случае перед запуском необходимо инициализировать ResourceLoader. Теперь увеличьте масштаб этого кошмара зависимостей, и вы, вероятно, сможете догадаться, что произойдет. Представьте, что вы пытаетесь поддерживать код без явного порядка инициализации. Сравните это с внедрением зависимостей с экземплярами - в этом случае код даже не будет компилироваться, если зависимости не выполняются!
Более того, если вы используете статику, изменяющую состояние, это похоже на карточный домик. Никогда не знаешь, у кого к чему есть доступ, а дизайн имеет тенденцию напоминать монстра-спагетти.
Наконец, что не менее важно, использование статики связывает программу с конкретной реализацией. Статический код - это полная противоположность тестируемости. Тестирование кода, пронизанного статикой, - это кошмар. Статический вызов никогда не может быть заменен тестовым двойником (если вы не используете тестовые фреймворки, специально разработанные для имитации статических типов), поэтому статическая система заставляет все, что ее использует, быть тестом мгновенной интеграции.
Короче говоря, статика подходит для некоторых вещей, а для небольших инструментов или одноразового кода я бы не стал препятствовать их использованию. Однако, помимо этого, они представляют собой кровавый кошмар для ремонтопригодности, хорошего дизайна и простоты тестирования.
Вот хорошая статья о проблемах: http://gamearchitect.net/2008/09/13/an-anatomy-of-despair-managers-and-contexts/