Я продолжаю сталкиваться с людьми, которые предостерегают от использования Синглтона и рекомендую избегать его как чумы. Что в этом страшного?
— Кайлаш Баду
Это хороший вопрос, потому что это правда, что глобальные переменные часто демонизируются, и в последнее время Синглтон постигла та же участь. Возможно, это немного удивительно, но найти достаточно веские аргументы в поддержку этих общих знаний очень сложно — поиск в Google приводит к путанице.
Поскольку я часто занимал эту позицию сам, я счел разумным, что я должен быть в состоянии отстаивать это, поэтому я попытаюсь дать объяснение. Это также отчасти продолжение моего поста с прошлой недели , в котором я представляю способ избежать глобальных символов, не тратя много времени на то, почему .
Анатомия Синглтона
Некоторая путаница вокруг Singletons происходит из-за того, что это проявление двух разных концепций, каждое из которых отрицательно влияет на архитектуру приложений. Это запутывает случай, так как люди имеют тенденцию иметь дело только с одним или другим из этих двух.
Наиболее очевидным из этих понятий является то, что синглтон является формой глобального государства . Это имеет общее с глобальными переменными , а также с парой других — менее очевидных — проявлений этого.
Другая концепция, которую содержит Singleton, — это статическое связывание . Оба из них тесно связаны с областью действия, что опять-таки связано с проведением четких границ между частями программы.
Но я забегаю вперед.
Состояние программы
Возможно, самый простой способ понять программу, это с точки зрения данных и кода . Эти два существуют в разных мирах, с разными правилами. Когда код выполняется, он манипулирует данными, производя какой-то результат. Данные в программе, известны как состояние.
Поскольку основной единицей программы является функция, состояние обычно относится к данным, которые относятся к функции. Функция называется состоящей из состояний, если она меняет свой контекст, при запуске или если контекст оказывает какое-либо влияние на результат функции. Иногда термин «побочные эффекты» используется для описания того же. Функция называется чистой , если она всегда выдает один и тот же вывод при одинаковом вводе. Например, следующее является чистой функцией:
function add($a, $b) {
return $a + $b;
}
В той же терминологии функция, которая не является чистой, называется процедурой . Отсюда и термин « процедурное программирование» . Ниже приведен пример процедуры:
function add($a, $b) {
echo ($a + $b);
}
Разные оттенки состояния
Глобальные переменные — это определенный тип состояния программы, но он не единственный. Приведенный выше пример (2) имеет побочный эффект без использования глобальной переменной — он записывает в стандартный вывод. Другой распространенный тип состояния — это изменчивые объекты. Например, объект, имеющий setName()
Даже локальная переменная является состоящей из тела функции. Хотя это все примеры состояния, они представляют разные степени на шкале.
В чем проблема, опять же?
Как вы уже догадались из терминологии (чисто => хорошо, побочные эффекты => плохо), подразумевается, что состояние плохое.
Последствия побочных эффектов являются двоякими. С одной стороны, они вызывают сложность. Проблема в том, что ввод / вывод не является явным. Глядя только на интерфейс, невозможно узнать, что происходит при вызове функции. Это может быть небольшой проблемой, но она увеличивается по мере роста программы.
Другая проблема — это проблема сцепления. Поскольку состояние может совместно использоваться различными функциями, оно может вводить зависимость между этими функциями. Например, если классы A
B
G
G
A
B
Этот вид зависимости называется связью .
В программах есть средства от чрезмерного состояния: глобальные переменные могут быть заменены локальными. Локальные переменные можно заменить вызовами функций ( замените temp запросом ). Объекты, которые передаются, можно сделать неизменными — это особенно важно для долгоживущих объектов, которые взаимодействуют со многими сторонами .
Статическая привязка
Программа имеет два разных типа существования; Время компиляции и время выполнения. Интерпретируемые языки чередуются между ними, в то время как скомпилированный язык имеет более строгое разделение. В любом случае, символы программы, которые принимают значение во время компиляции, не могут изменить его значение во время выполнения. Примерами таких символов являются константы, литералы, имена классов и статические методы (и в PHP плавающие функции ). Символы времени выполнения (переменные), с другой стороны, могут менять свое значение.
Несмотря на то, что статические символы не могут измениться, они все же могут влиять на результат функции, поэтому в очень общем смысле они являются формой состояния. Обычно не так важно избегать состояния статических символов, но в некоторых случаях они могут вызывать те же проблемы, что и состояние во время выполнения. Например, вызовы статических методов вызывают статическую (во время компиляции) связь. Синглтон является ярким примером такого рода связи; Если в классе A используется синглтон S, то существует сильная связь между A и S.
связь
Сцепление, конечно, неизбежно и в некоторой степени даже желательно. Однако, как и во время выполнения, ограничение — хорошая идея. Проблема со статической связью состоит в том, что это затрудняет (или даже делает невозможным) повторное использование компонента изолированно от других компонентов, с которыми он связан. Обычно это не является непосредственной проблемой, но может оказать влияние на техническое обслуживание. Это также затрудняет юнит-тестирование и отладку кода, потому что невозможно выделить часть вашей программы.
Это все
Я только немного коснулся темы, но надеюсь, что это дает некоторое объяснение тому, почему глобалы так не одобряются. Я просто оставлю вам следующую цитату:
Прости меня, отец, потому что у меня есть Singleton’ed
— Кевлин Хенни
Комментарии — как всегда — приветствуются, но, пожалуйста, воздержитесь от откусывания голов (моих или других).