Я обращаюсь к своей базе данных MySQL через PDO. Я настраиваю доступ к базе данных, и моя первая попытка заключалась в следующем:
Первое, о чем я думал, – global
:
$db = new PDO('mysql:host=127.0.0.1;dbname=toto', 'root', 'pwd'); function some_function() { global $db; $db->query('...'); }
Это считается плохой практикой. После небольшого поиска я оказался с шаблоном Синглтона , который
«применяется к ситуациям, в которых должен быть один экземпляр класса».
Согласно примеру в руководстве, мы должны сделать следующее:
class Database { private static $instance, $db; private function __construct(){} static function singleton() { if(!isset(self::$instance)) self::$instance = new __CLASS__; return self:$instance; } function get() { if(!isset(self::$db)) self::$db = new PDO('mysql:host=127.0.0.1;dbname=toto', 'user', 'pwd') return self::$db; } } function some_function() { $db = Database::singleton(); $db->get()->query('...'); } some_function();
Зачем мне этот относительно большой класс, когда я могу это сделать?
class Database { private static $db; private function __construct(){} static function get() { if(!isset(self::$rand)) self::$db = new PDO('mysql:host=127.0.0.1;dbname=toto', 'user', 'pwd'); return self::$db; } } function some_function() { Database::get()->query('...'); } some_function();
Этот последний работает отлично, и мне больше не нужно беспокоиться о $db
.
Как я могу создать меньший одноэлементный класс, или есть прецедент для одиночных игр, которые мне не хватает в PHP?
Хорошо, я подумал об этом некоторое время, когда я впервые начал свою карьеру. Реализовала его разными способами и придумала две причины, чтобы не использовать статические классы, но они довольно большие.
Во-первых, вы обнаружите, что очень часто то, что вы абсолютно уверены, что у вас никогда не будет более одного экземпляра, вы, в конце концов, имеете вторую. У вас может быть второй монитор, вторая база данных, второй сервер – что угодно.
Когда это произойдет, если вы использовали статический класс, вы используете гораздо худший рефакторинг, чем если бы вы использовали синглтон. Синглтон – это шаблон iffy сам по себе, но он довольно легко преобразуется в интеллектуальный заводский шаблон – даже может быть преобразован для использования инъекции зависимостей без особых проблем. Например, если ваш singleton получен через getInstance (), вы можете довольно легко изменить это на getInstance (имя базы данных) и разрешить несколько баз данных – никаких других изменений кода.
Вторая проблема – тестирование (и, честно говоря, это то же самое, что и первая проблема). Иногда вы хотите заменить свою базу данных макетной базой данных. Фактически это второй экземпляр объекта базы данных. Это гораздо сложнее сделать со статическими классами, чем с одноэлементным, вам нужно только измотать метод getInstance (), а не каждый метод в статическом классе (который на некоторых языках может быть очень сложным).
Это действительно сводится к привычкам – и когда люди говорят, что «Глобалы» плохи, у них есть веские причины говорить об этом, но это может быть не всегда очевидно, пока вы сами не столкнетесь с проблемой.
Лучшее, что вы можете сделать, это спросить (как вы это сделали), затем сделать выбор и понаблюдать за последствиями вашего решения. Наличие знания для интерпретации эволюции вашего кода с течением времени гораздо важнее, чем в первую очередь.
В языках, где объекты живут в разделяемой памяти, Singletons можно использовать, чтобы поддерживать низкий уровень использования памяти. Вместо создания двух объектов вы ссылаетесь на существующий экземпляр из глобальной памяти приложений. В PHP нет такой памяти приложения. Синглтон, созданный в одном запросе, живет именно для этого запроса. Синглтон, созданный в другом Запросе, выполненном одновременно, по-прежнему является совершенно другим экземпляром. Таким образом, одна из двух основных целей Singleton здесь не применима.
Кроме того, многие объекты, которые могут концептуально существовать только один раз в вашем приложении, не обязательно требуют языкового механизма для обеспечения этого. Если вам нужен только один экземпляр, то не создавайте экземпляр другого . Только тогда, когда у вас нет другого экземпляра, например, когда котята умирают, когда вы создаете второй экземпляр, вы можете иметь действительный пример использования для Singleton.
Другой целью было бы иметь глобальную точку доступа к экземпляру внутри одного и того же запроса. Хотя это может показаться желательным, на самом деле это не так, потому что оно создает связь с глобальным охватом (например, любые глобальные и статические). Это упрощает тестирование устройств и ваше приложение в целом менее ремонтопригодным. Есть способы смягчить это, но в целом, если вам нужно иметь один и тот же экземпляр во многих классах, используйте Injection Dependency .
См. Мои слайды для Singletons в PHP. Почему они плохи и как их можно устранить из своих приложений для получения дополнительной информации.
Даже Эрих Гамма , один из изобретателей модели Синглтона, сейчас сомневается в этом шаблоне:
«Я за то, что выбрал Singleton, его использование почти всегда является запахом дизайна»,
Синглтоны считаются многими анти-шаблонами, поскольку они действительно просто прославили глобальные переменные. На практике существует относительно мало сценариев, когда необходимо, чтобы класс имел только один экземпляр; обычно достаточно, чтобы один экземпляр был достаточным , и в этом случае реализовать его как одноэлемент совершенно не нужно.
Чтобы ответить на вопрос, вы правы, что одиночные игры здесь излишни. Простая переменная или функция. Однако лучшим (более надежным) подходом было бы использование инъекции зависимостей для полного устранения глобальных переменных.
В вашем примере вы имеете дело с одной частью, казалось бы, неизменной информации. Для этого примера Синглтон был бы излишним, и просто использование статической функции в классе будет прекрасно.
Больше мыслей. Возможно, вы испытываете случай внедрения шаблонов для шаблонов, и ваша кишка говорит вам «нет, вам не обязательно» по причинам, которые вы указали.
НО: Мы не имеем представления о размере и объеме вашего проекта. Если это простой код, возможно, выбросить, что вряд ли нужно будет изменить, тогда да, идите и используйте статические члены. Но если вы считаете, что вашему проекту может потребоваться масштабирование или подготовка к кодированию обслуживания вниз по дороге, то да, вы можете использовать шаблон Singleton.
Во-первых, я просто хочу сказать, что я не очень много использую шаблон Singleton. Зачем нужно, чтобы один объект полностью обрабатывал все приложение? Специально для баз данных, что делать, если я хочу подключиться к другому серверу базы данных? Я должен каждый раз отключать и снова подключаться …? Так или иначе…
Есть несколько недостатков в использовании глобальных приложений в приложении (что традиционное использование шаблона Singleton):
Использование статических классов вместо экземпляра singleton также дает некоторые из тех же недостатков, потому что самая большая проблема singleton – это статический метод getInstance
.
Вы можете ограничить количество экземпляров, которые может иметь класс, не используя традиционный метод getInstance
:
class Single { static private $_instance = false; public function __construct() { if (self::$_instance) throw new RuntimeException('An instance of '.__CLASS__.' already exists'); self::$_instance = true; } private function __clone() { throw new RuntimeException('Cannot clone a singleton class'); } public function __destruct() { self::$_instance = false; } } $a = new Single; $b = new Single; // error $b = clone($a); // error unset($a); $b = new Single; // works
сclass Single { static private $_instance = false; public function __construct() { if (self::$_instance) throw new RuntimeException('An instance of '.__CLASS__.' already exists'); self::$_instance = true; } private function __clone() { throw new RuntimeException('Cannot clone a singleton class'); } public function __destruct() { self::$_instance = false; } } $a = new Single; $b = new Single; // error $b = clone($a); // error unset($a); $b = new Single; // works
Это поможет в первую очередь в упомянутых выше пунктах: модульное тестирование и инъекция зависимостей; при этом убедитесь, что в вашем приложении существует один экземпляр класса. Вы могли бы, например, просто передать результирующий объект вашим моделям (шаблон MVC) для их использования.
Подумайте, как ваше решение отличается от того, которое представлено в документах PHP. Фактически, существует только одна «маленькая» разница: ваше решение предоставляет вызывающим абонентам получателя с экземпляром PDO
, в то время как в документах содержатся вызовы Database::singleton
с экземпляром Database
(они затем используют getter для этого получить экземпляр PDO
).
Итак, какой вывод мы достигли?
Database
. Класс Database
может выставлять (на самом деле, он должен разоблачить, если вы перейдете ко всем этим проблемам) более богатый или более высокий уровень интерфейса, чем объект PDO
он обертывает. PDO
, то две реализации эквивалентны. Нет никакой выгоды от выполнения ручной версии. С практической стороны, Синглтон – довольно спорный образец. Это происходит главным образом потому, что:
Итак, как окончательный вывод: ваш синглтон в порядке. Не использовать Singleton вообще просто отлично в большинстве случаев.
Ваша интерпретация верна. Синглтоны имеют свое место, но злоупотребляют. Часто доступ к статическим функциям-членам достаточно (особенно, когда вам не нужно каким-либо образом контролировать время создания). Лучше, вы можете просто поместить некоторые свободные функции и переменные в пространство имен.
При программировании нет «правильных» и «неправильных»; есть «хорошая практика» и «плохая практика».
Синглтоны обычно создаются как класс для повторного использования позже. Они должны быть созданы таким образом, чтобы программист не случайно создавал экземпляры двух экземпляров, когда пьяный кодировался в полночь.
Если у вас есть простой маленький класс, который не следует создавать несколько раз, вам не нужно делать его синглом. Если вы это сделаете, это просто защитная сетка.
не всегда плохая практика иметь глобальные объекты. Если вы знаете, что собираетесь использовать его глобально / везде / все время, это может быть одно из немногих исключений. Однако глобалы обычно считаются «плохой практикой» таким же образом, что goto
считается плохой практикой.
Я не вижу в этом никакого смысла. Если вы внедрили класс таким образом, чтобы строка соединения была взята в качестве параметра конструктору и поддерживала список объектов PDO (по одному для каждой уникальной строки соединения), то, возможно, была бы какая-то польза, но реализация singleton в этот случай кажется бессмысленным.
Насколько мне известно, вы ничего не пропустили. Пример довольно ошибочен. Это будет иметь значение, если в классе singleton были некоторые нестатические переменные экземпляра.