Вкратце, это делает состояние программы непредсказуемым.
Чтобы уточнить, представьте, что у вас есть пара объектов, которые используют одну и ту же глобальную переменную. Предполагая, что вы не используете источник случайности ни в одном из модулей, выход конкретного метода можно предсказать (и, следовательно, протестировать), если состояние системы известно до выполнения метода.
Однако, если метод в одном из объектов вызывает побочный эффект, который изменяет значение общего глобального состояния, вы больше не знаете, что такое начальное состояние, когда выполняете метод в другом объекте. Теперь вы больше не можете предсказать, какой вывод вы получите при выполнении метода, и, следовательно, вы не можете его протестировать.
На академическом уровне это может показаться не таким уж серьезным, но возможность модульного тестирования кода является важным шагом в процессе доказательства его правильности (или, по крайней мере, соответствия цели).
В реальном мире это может иметь очень серьезные последствия. Предположим, у вас есть один класс, который заполняет глобальную структуру данных, и другой класс, который использует данные в этой структуре данных, изменяя их состояние или уничтожая их в процессе. Если класс процессора выполняет метод до того, как будет создан класс популяции, в результате класс процессора, вероятно, будет иметь неполные данные для обработки, а структура данных, над которой работал класс популяции, может быть повреждена или уничтожена. Поведение программы в этих условиях становится совершенно непредсказуемым и, вероятно, приведет к эпической потере.
Кроме того, глобальное состояние ухудшает читабельность вашего кода. Если у вашего кода есть внешняя зависимость, которая явно не введена в код, тогда любой, кто получит работу по поддержке вашего кода, должен будет искать его, чтобы выяснить, откуда он взялся.
Что касается того, какие альтернативы существуют, то вообще невозможно вообще не иметь глобального состояния, но на практике обычно можно ограничить глобальное состояние одним объектом, который охватывает все остальные, и на который никогда нельзя ссылаться, полагаясь на правила области видимости. языка, который вы используете. Если конкретному объекту необходимо определенное состояние, он должен явно запросить его, передав его в качестве аргумента своему конструктору или с помощью метода установки. Это известно как инъекция зависимости.
Может показаться глупым переходить в состояние, к которому вы уже можете получить доступ из-за правил области видимости любого языка, который вы используете, но преимущества огромны. Теперь, если кто-то смотрит на код изолированно, становится ясно, в каком состоянии он нуждается и откуда он поступает. Он также имеет огромные преимущества в отношении гибкости вашего модуля кода и, следовательно, возможности его повторного использования в различных контекстах. Если состояние передается и изменения состояния являются локальными для блока кода, то вы можете передать любое состояние, которое вам нравится (если это правильный тип данных), и ваш код его обработает. Код, написанный в этом стиле, имеет тенденцию выглядеть как набор свободно связанных компонентов, которые можно легко заменить. Код модуля не должен заботиться о том, откуда приходит состояние, а только о том, как его обрабатывать.
Существует множество других причин, по которым переходящее состояние намного превосходит доверие к глобальному состоянию. Этот ответ ни в коем случае не является исчерпывающим. Возможно, вы могли бы написать целую книгу о том, почему глобальное состояние плохо.