Статические классы в порядке, если они используются в правильных местах.
А именно: Методы, которые являются «листовыми» методами (они не изменяют состояние, они каким-то образом преобразуют вход). Хорошими примерами этого являются такие вещи, как 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/
Это была именно та проблема, которую я должен был решить - использование или, скорее, неправильное использование объектов Singleton. – overslacked
Спасибо за этот отличный ответ. Мой вопрос в том, что если синглтоны передаются как параметры статическим методам, делает ли это статический метод небезопасным? –
Термины «чистая функция» и «нечистая функция» - это имена, приведенные в функциональном программировании, к тем, что вы называете «безопасными» и «небезопасными» статическими. – Omnimike