Есть ли сценарий использования для синглетонов с доступом к базе данных в PHP? - PullRequest
136 голосов
/ 04 января 2011

Я получаю доступ к своей базе данных MySQL через PDO. Я настраиваю доступ к базе данных, и моей первой попыткой было использовать следующее:

Первое, о чем я подумал, это global:

$db = new PDO('mysql:host=127.0.0.1;dbname=toto', 'root', 'pwd');

function some_function() {
    global $db;
    $db->query('...');
}

Это считается плохой практикой. После небольшого поиска я получил шаблон Singleton , который

"применяется к ситуациям, в которых должен быть один экземпляр класса."

В соответствии с примером в руководстве мы должны сделать следующее:

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::$db))
            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?

Ответы [ 11 ]

317 голосов
/ 04 января 2011

У синглетонов очень мало - если не сказать нет - использовать в PHP.

На языках, где объекты живут в разделяемой памяти, Singletons можно использовать для поддержания низкого уровня использования памяти. Вместо создания двух объектов вы ссылаетесь на существующий экземпляр из общей памяти приложения. В PHP нет такой памяти приложения. Синглтон, созданный в одном запросе, живет именно для этого запроса. Синглтон, созданный в другом запросе, выполненном в то же время, все еще является совершенно другим экземпляром. Таким образом, одно из двух основных назначений синглтона здесь не применимо.

Кроме того, многим объектам, которые могут концептуально существовать в вашем приложении только один раз, не обязательно требуется языковой механизм для обеспечения этого. Если вам нужен только один экземпляр, тогда не создавайте экземпляр другого . Только когда у вас может не быть другого экземпляра, например когда котята умирают при создании второго экземпляра, у вас может быть действительный вариант использования для синглтона.

Другая цель состояла бы в том, чтобы иметь глобальную точку доступа к экземпляру в том же запросе. Хотя это может показаться желательным, на самом деле это не так, потому что это создает связь с глобальной областью действия (как и любые глобальные и статические). Это усложняет юнит-тестирование , а ваше приложение в целом будет проще в обслуживании. Есть способы смягчить это, но в целом, если вам нужно иметь один и тот же экземпляр во многих классах, используйте Внедрение зависимостей .

См. Мои слайды для Singletons в PHP - почему они плохие и как вы можете исключить их из своих приложений для получения дополнительной информации.

Даже Эрих Гамма , один из изобретателей модели Синглтона, в настоящее время сомневается в этой модели:

«Я за то, чтобы отбросить синглтон. Его использование почти всегда вызывает запах дизайна»

Дальнейшее чтение

Если после вышесказанного вам все еще нужна помощь в принятии решения:

Singleton Decision Diagram

79 голосов
/ 04 января 2011

Хорошо, я какое-то время размышлял над этим, когда только начинал свою карьеру. Реализовал его по-разному и придумал две причины отказаться от использования статических классов, но они довольно большие.

Во-первых, вы обнаружите, что очень часто что-то, в чем вы абсолютно уверены, что у вас никогда не будет более одного экземпляра, у вас в конечном итоге будет второй. Вы можете получить второй монитор, вторую базу данных, второй сервер - что угодно.

Когда это происходит, если вы использовали статический класс, вы получаете гораздо худший рефакторинг, чем если бы вы использовали синглтон. Синглтон сам по себе является сомнительным шаблоном, но он довольно легко преобразуется в интеллектуальный фабричный шаблон - даже может быть преобразован для использования внедрения зависимостей без особых проблем. Например, если ваш синглтон получен через getInstance (), вы можете довольно легко изменить его на getInstance (databaseName) и разрешить использование нескольких баз данных - никаких других изменений кода.

Второй вопрос - тестирование (И, честно говоря, это то же самое, что и первый выпуск). Иногда вы хотите заменить вашу базу данных фиктивной базой данных. По сути это второй экземпляр объекта базы данных. Это гораздо сложнее сделать со статическими классами, чем с одноэлементными, вам нужно только смоделировать метод getInstance (), а не каждый отдельный метод в статическом классе (что в некоторых языках может быть очень сложным).

Это действительно сводится к привычкам - и когда люди говорят, что «Глобалы» - это плохо, у них есть очень веские причины для этого, но это не всегда очевидно, пока вы сами не решите проблему.

Лучшее, что вы можете сделать, это спросить (как вы это сделали), а затем сделать выбор и наблюдать последствия вашего решения. Знание того, как интерпретировать эволюцию вашего кода с течением времени, гораздо важнее, чем делать это правильно.

21 голосов
/ 01 ноября 2012

Кому нужны синглтоны в PHP?

Обратите внимание, что почти все возражения против синглетонов исходят с технической точки зрения - но они также ОЧЕНЬ ограничены в своих масштабах.Специально для PHP.Сначала я перечислю некоторые причины использования синглетонов, а затем проанализирую возражения против их использования.Во-первых, люди, которые нуждаются в них:

- Люди, которые кодируют большую платформу / кодовую базу, которая будет использоваться во многих различных средах, должны будут работать с ранее существовавшими различными фреймворками / кодовыми базами, снеобходимость выполнения множества различных, изменяющихся, даже причудливых запросов от клиентов / начальников / руководителей / руководителей подразделений.

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

- Люди, которым нужно работать с API сторонних производителей , сервисами ивеб-сайты.

Если вы посмотрите поближе, это не слишком отличается от предыдущего случая - сторонние API, сервисы, веб-сайты похожи на внешние, изолированные кодовые базы, над которыми вы НЕТ контроля.Все может случиться.Таким образом, с помощью одноэлементного сеанса / пользовательского класса вы можете управлять ЛЮБОЙ реализацией сеанса / авторизации от сторонних поставщиков, таких как OpenID , Facebook , Twitter имногие другие - и вы можете делать это ВСЕ одновременно из одного и того же объекта-одиночки - который легко доступен, в известном состоянии в любой точке любого кода, к которому вы подключаете его.Вы даже можете создать несколько сеансов для нескольких разных сторонних API / сервисов для ОДНОГО ЖЕ пользователя в своем собственном веб-сайте / приложении и делать с ними все, что хотите.

Конечно, все это такжеможет быть тон с традиционными методами, используя обычные классы и объекты - суть в том, что синглтон аккуратнее, аккуратнее и, следовательно, из-за того, что управляемый / тестируемый легче по сравнению с традиционным использованием классов / объектов в таких ситуациях.

- Люди, которым нужно быстрое развитие

Глобальное поведение синглетонов облегчает создание любого вида кода с каркасом, в котором есть набор синглетонов, потому что, как только вы хорошо сконструируете свои синглтон-классы, установившиеся, зрелые и установленные методы будутлегко доступны и доступны в любом месте, в любое время, в согласованном порядке.Созревание ваших классов занимает некоторое время, но после этого они становятся твердыми, последовательными и полезными.Вы можете иметь столько методов в одном объекте, которые делают все, что вы хотите, и, хотя это может увеличить объем памяти объекта, это приносит гораздо большую экономию времени, необходимого для быстрой разработки - метод, который вы не используете в одном конкретном случаеприложение может быть использовано в другом интегрированном приложении, и вы можете просто добавить новую функцию, которую клиент / руководитель / менеджер проекта запрашивает всего несколькими изменениями.

Вы поняли идею.Теперь давайте перейдем к возражениям против синглетонов и нечестивому крестовому походу против чего-то полезного :

- Главное возражение состоит в том, что это затрудняет тестирование.

И действительно, в некоторой степени это происходит, даже если его можно легко смягчить, приняв надлежащие меры предосторожности и запрограммировав процедуры отладки в свои синглтоны С осознанием того, что вы будете отлаживать синглтон.Но, видите, это не слишком отличается от ЛЮБОЙ другой философии / метода / паттерна кодирования, которая существует - просто синглтоны относительно новы и не получили широкого распространения, поэтому современные методы тестирования оказываются сравнительно несовместимыми с ними.Но это не отличается ни в одном аспекте языков программирования - разные стили требуют разных подходов.

С одной стороны, это возражение не имеет смысла, поскольку оно игнорирует тот факт, что разрабатываемые приложения не предназначены для «тестирования», и тестирование - не единственная фаза / процесс, который входит в разработку приложения.Приложения разрабатываются для производственного использования.И, как я объяснил в разделе «кому нужны синглтоны», синглтоны могут сделать БОЛЬШУЮ сделку из-за сложности заставить код работать С И ВНУТРИ многих различных баз кода / приложений / сторонних сервисов.Время, которое может быть потеряно при тестировании, - это время, затраченное на разработку и развертывание.Это особенно полезно в эпоху сторонней аутентификации / приложения / интеграции - Facebook, Twitter, OpenID, многих других и кто знает, что будет дальше.

Хотя это и понятно - программисты работают в самых разных обстоятельствах в зависимости отих карьера.А для людей, которые работают в относительно крупных компаниях с определенными отделами, которые комфортно работают с разными, определенными программами / приложениями и без надвигающейся гибели / увольнений в бюджете и сопутствующей необходимости делать МНОГО вещей с множеством разных вещей вдешевый / быстрый / надежный способ, одиночные игры могут показаться не столь необходимыми.И это может даже быть неприятностью / препятствием к тому, что они УЖЕ имеют.

Но для тех, кому нужно работать в грязных канавах «гибкой» разработки, когда приходится выполнять много разных запросов (иногда необоснованных) от их клиента / менеджера / проекта, синглтоны - это спасительная грация по причинам, объясненнымранее.

- Другое возражение состоит в том, что его объем памяти выше

Поскольку новый синглтон будет существовать для каждого запроса от каждого клиента, это МОЖЕТ быть возражением для PHP,С плохо сконструированными и используемыми синглетами объем памяти приложения может быть выше, если приложение обслуживает много пользователей в любой заданной точке.

Хотя это справедливо для ЛЮБОГО подхода, который вы можете использовать при кодировании.Вопросы, которые следует задать, являются ли ненужными методы, данные, которые хранятся и обрабатываются этими единицами?Поскольку, если они необходимы во многих запросах, которые получает приложение, то даже если вы не используете синглтоны, эти методы и данные будут присутствовать в вашем приложении в той или иной форме через код.Таким образом, все становится вопросом того, сколько памяти вы будете экономить, когда вы инициализируете традиционный объект класса 1/3 в обработку кода и уничтожаете его 3/4 в него.

Видите ли, если поставить так, вопрос становится совершенно неактуальным - не должно быть ненужных методов, данных, содержащихся в объектах в вашем коде, В любом случае - независимо от того, используете вы синглтоны или нет.Таким образом, это возражение против синглетонов становится действительно смешным, поскольку оно предполагает, что будут ненужные методы, данные в объектах, созданных из используемых вами классов.

- Некоторые недопустимые возражения, такие как «делает невозможным / более сложным поддержание нескольких соединений с базой данных»

Я даже не могу понять это возражение, когда нужно поддерживать всенесколько соединений с базой данных, несколько вариантов выбора базы данных, несколько запросов к базе данных, несколько наборов результатов в заданном синглтоне просто хранят их в переменных / массивах в синглтоне столько времени, сколько они необходимы.Это может быть так же просто, как хранить их в массивах, хотя вы можете изобрести любой метод, который вы хотите использовать для достижения этой цели.Но давайте рассмотрим простейший случай, использование переменных и массивов в данном синглтоне:

Представьте, что ниже находится внутри данного синглтона базы данных:

$ this -> соединения= массив (); (неправильный синтаксис, я просто набрал его так, чтобы дать вам картинку - правильное объявление переменной public $ connections = array (); и ее использование, естественно, $ this-> connections ['connectionkey'])

Таким способом вы можете установить и сохранить несколько соединений в любой момент времени в массиве.И то же самое касается запросов, наборов результатов и т. Д.

$ this -> запрос (QUERYSTRING, 'queryname', $ this-> connections ['partulrconnection ']);

, который может просто выполнить запрос к выбранной базе данных с выбранным соединением и просто сохранить в вашем

$ this -> результаты

с помощьюключ 'queryname'.Конечно, для этого вам потребуется кодирование вашего метода запроса - это тривиально.

Это позволяет вам поддерживать практически бесконечное количество (насколько позволяют, конечно, ограничения ресурса) другой базы данных.соединения и наборы результатов столько, сколько вам нужно.И они доступны для ЛЮБОГО фрагмента кода в любой заданной точке любой заданной кодовой базы, в которую был создан экземпляр этого синглтон-класса.

OF COURSE, вам, естественно, потребуется освободить результирующие наборы и соединения, когда они не нужны- но это само собой разумеется, и оно не является специфичным для синглтонов или любого другого метода / стиля / концепции кодирования.

На этом этапе вы можете увидеть, как вы можете поддерживать несколько соединений / состояний со сторонними приложениями илиуслуги в том же синглтоне.Не так уж и отличается.

Короче говоря, одноэлементные паттерны - это просто еще один метод / стиль / философия для программирования, и они так же полезны, как ЛЮБЫЕ другие, когда используются в правильном месте, в правильной манере.Что ничем не отличается от всего.

Вы заметите, что в большинстве статей, в которых разбиты синглтоны, вы также увидите ссылки на «глобальные» слова «злые».

Давайте посмотрим правде в глаза - ЛЮБОЕ, что не используется должным образом, злоупотребляет, злоупотребляет, является злом.Это не ограничивается каким-либо языком, любой концепцией кодирования, любым методом.Всякий раз, когда вы видите кого-то, кто произносит общие заявления типа «X - зло», убегайте от этой статьи.Очень высоки шансы, что это продукт ограниченной точки зрения - даже если эта точка зрения является результатом многолетнего опыта в чем-то конкретном, что обычно заканчивается результатом слишком большой работы в данном стиле / методе - типичном интеллектуальном консерватизме.

Для этого можно привести бесконечные примеры, начиная от «глобальных злых» и заканчивая «фреймами злых».Еще около 10 лет назад даже предложение использовать iframe в любом конкретном приложении было ересью.Затем идет Facebook, везде фреймы, и посмотрите, что произошло - фреймы уже не так злы.

1106 * Есть еще люди, которые упорно настаивают на том, что они являются «злом», - а иногда и по уважительной причине тоже - но, как вы можете видеть, есть необходимость, плавающие фреймы заполнить эту потребность и хорошо работать, и, следовательно,весь мир просто движется.

Главным преимуществом программиста / программиста / программиста является свободный, открытый и гибкий ум.

15 голосов
/ 04 января 2011

Синглтоны, как полагают многие, являются анти-паттернами , поскольку на самом деле они просто прославляют глобальные переменные.На практике существует относительно немного сценариев, в которых необходимо , чтобы класс имел только один экземпляр;обычно просто один экземпляр достаточен , и в этом случае его реализация в виде синглтона совершенно не нужна.

Чтобы ответить на вопрос, вы правы, что синглтоны здесь излишни.Подойдет простая переменная или функция.Однако лучше (более надежный) подход будет использовать внедрение зависимостей , чтобы полностью исключить необходимость в глобальных переменных.

8 голосов
/ 04 января 2011

В вашем примере вы имеете дело с одной, казалось бы, неизменной информацией.В этом примере синглтон был бы излишним, и просто использование статической функции в классе вполне подойдет.

Больше мыслей: у вас может быть случай реализации шаблонов ради шаблонов, и ваша интуиция говоритВы «нет, вам не нужно» по причинам, которые вы изложили.

НО: Мы не имеем представления о размере и масштабах вашего проекта.Если это простой код, возможно, отброшенный, который вряд ли нужно менять, тогда да, используйте статические члены.Но если вы считаете, что вашему проекту может потребоваться масштабирование или подготовка к техническому кодированию в будущем, тогда, возможно, вы захотите использовать шаблон Singleton.

5 голосов
/ 04 января 2011

Просто подумайте, чем ваше решение отличается от представленного в документации по PHP. Фактически, есть только одно «маленькое» отличие: ваше решение предоставляет вызывающим объектам getter экземпляр PDO, в то время как тот, что в документах, предоставляет вызывающим Database::singleton экземпляр Database (затем они используют метод getter). на это, чтобы получить PDO экземпляр).

Так к какому выводу мы пришли?

  • В коде документации вызывающие абоненты получают экземпляр Database. Класс Database может предоставить (на самом деле, должен показать, если вы собираетесь столкнуться со всеми этими проблемами) более богатый интерфейс или интерфейс более высокого уровня, чем объект PDO, который он переносит.
  • Если вы измените свою реализацию так, чтобы она возвращала другой (более богатый) тип, чем PDO, тогда эти две реализации эквивалентны. Нет никакой выгоды от следования ручной реализации.

С практической точки зрения, Singleton - довольно противоречивая модель. Это в основном потому, что:

  • Это чрезмерно. Начинающим программистам грохнуть синглтон гораздо проще, чем другим моделям. Затем они продолжают применять свои вновь обретенные знания повсюду, даже если проблему можно решить лучше без Синглтона (когда вы держите молоток, все выглядит как гвоздь).
  • В зависимости от языка программирования, реализация Singleton в герметичной, неплотной манере может оказаться колоссальной задачей (особенно, если у нас есть продвинутые сценарии: одиночка в зависимости от другого singleton, синглеты, которые могут быть уничтожены и повторно создано и т. д.). Просто попытайтесь найти «окончательную» реализацию Singleton в C ++, я вас осмелюсь (у меня есть революционный Modern C ++ Design Андрея Александреску, который документирует большую часть беспорядка).
  • Это создает дополнительную рабочую нагрузку как при кодировании Singleton, так и при написании кода для доступа к нему, рабочую нагрузку, без которой вы можете следовать, выполнив несколько наложенных самостоятельно ограничений на то, что вы пытаетесь делать с переменными вашей программы.

Итак, в заключение: ваш синглтон просто в порядке. Если вы вообще не используете Singleton, то в большинстве случаев это нормально.

5 голосов
/ 04 января 2011

Во-первых, я просто хочу сказать, что я не нахожу особого смысла в паттерне Синглтона.Зачем кому-то нужен один объект для всего приложения?Что делать, если я хочу подключиться к другому серверу баз данных?Я должен отключаться и подключаться каждый раз ...?В любом случае ...

Существует несколько недостатков использования глобалов в приложении (что и делает традиционное использование шаблона 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

Это поможетпо первым пунктам, упомянутым выше: юнит-тестирование и внедрение зависимостей;по-прежнему следя за тем, чтобы в вашем приложении существовал один экземпляр класса.Например, вы можете просто передать полученный объект вашим моделям (шаблон MVC) для их использования.

2 голосов
/ 04 января 2011

При программировании нет «правильного» и «неправильного»; есть «хорошая практика» и «плохая практика».

Синглтоны обычно создаются как класс для последующего повторного использования. Они должны быть созданы таким образом, чтобы программист не мог случайно создать два экземпляра во время пьяного кодирования в полночь.

Если у вас есть простой маленький класс, для которого не следует создавать более одного раза, вам не нужно , чтобы сделать его одиночным. Это просто защитная сетка, если вы делаете.

Это не всегда плохая практика иметь глобальные объекты. Если вы знаете, что собираетесь использовать его глобально / везде / постоянно, это может быть одним из немногих исключений. Однако глобальные переменные обычно считаются «плохой практикой» так же, как goto считается плохой практикой.

2 голосов
/ 04 января 2011

Ваша интерпретация верна. У синглетонов есть свое место, но они чрезмерно используются. Часто достаточно доступа к статическим функциям-членам (особенно, когда вам не нужно каким-либо образом контролировать время создания). Более того, вы можете просто поместить несколько свободных функций и переменных в пространство имен.

2 голосов
/ 04 января 2011

Я вообще не вижу в этом смысла. Если вы реализовали класс таким образом, чтобы строка соединения была принята в качестве параметра для конструктора и поддерживала список PDO объектов (по одному для каждой уникальной строки соединения), то, возможно, будет некоторое преимущество, но реализация синглтона в этом случае кажется бессмысленным упражнением.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...