Глобальний чи Singleton для підключення до бази даних?


81

У чому перевага використання одиночного замість глобального для підключення до бази даних у PHP? Я відчуваю, що використання singleton замість global робить код надмірно складним.

Код із глобальним

$conn = new PDO(...);

function getSomething()
{
    global $conn;
    .
    .
    .
}

Код із Сінглтоном

class DB_Instance
{
    private static $db;

    public static function getDBO()
    {
        if (!self::$db)
            self::$db = new PDO(...);

        return self::$db;
    }
}

function getSomething()
{
    $conn = DB_Instance::getDBO();
    .
    .
    .
}

Якщо є кращий спосіб ініціалізації підключення до бази даних, відмінний від глобального чи одиночного, будь ласка, згадайте про це та опишіть його переваги перед глобальним чи одиночним.


Якщо ви плануєте використовувати PDO на сесії користувачів оброблювачів ви повинні знати про деякі особливості: stackoverflow.com/questions/2595860/pdo-prepare-silently-fails
Wabbitseason

Відповіді:


105

Я знаю, що це старе, але відповідь Dr8k майже була .

Коли ви плануєте написати фрагмент коду, припустимо, що він зміниться. Це не означає, що ви припускаєте, які саме зміни відбудуться в якийсь момент у майбутньому, а скоріше, що будуть внесені певні зміни.

Поставте це за мету, щоб пом’якшити біль, пов’язаний із внесенням змін у майбутньому: глобал небезпечний, тому що ним важко керувати в одному місці. Що робити, якщо я хочу повідомити цей контекст підключення до бази даних у майбутньому? Що робити, якщо я хочу, щоб він закривався і відкривався сам кожен 5-й раз, коли він був використаний. Що робити, якщо я вирішу, що в інтересах масштабування моєї програми я хочу використовувати пул з 10 з'єднань? Або конфігурована кількість з'єднань?

Одноточечного завод дає Вам таку гнучкість. Я налаштовую його з дуже малою додатковою складністю і отримую більше, ніж просто доступ до одного і того ж з'єднання; Я отримую можливість змінити спосіб передачі цього зв’язку мені згодом простою мовою.

Зверніть увагу, що я кажу про фабрику синглтон , на відміну від просто синглтона . Існує дорогоцінна невелика різниця між синглтоном і глобальним, правда. І через це немає жодних підстав для одностороннього з'єднання: чому б ви витрачали час на налаштування цього, коли замість цього могли створити звичайний глобальний?

Що фабрика отримує, це чому отримувати зв’язки та окреме місце, щоб вирішити, які зв’язки (або зв’язки) ви збираєтесь отримати.

Приклад

class ConnectionFactory
{
    private static $factory;
    private $db;

    public static function getFactory()
    {
        if (!self::$factory)
            self::$factory = new ConnectionFactory(...);
        return self::$factory;
    }

    public function getConnection() {
        if (!$this->db)
            $this->db = new PDO(...);
        return $this->db;
    }
}

function getSomething()
{
    $conn = ConnectionFactory::getFactory()->getConnection();
    .
    .
    .
}

Потім, через 6 місяців, коли ваш додаток стає надзвичайно відомим, і ви вирішили, що вам потрібно більше, ніж одне з’єднання, все, що вам потрібно зробити, це здійснити деякий пул у методі getConnection (). Або якщо ви вирішите, що вам потрібна обгортка, яка реалізує ведення журналу SQL, ви можете передати підклас PDO. Або якщо ви вирішили, що хочете нове підключення при кожному виклику, ви можете це зробити. Він гнучкий, а не жорсткий.

16 рядків коду, включаючи фігурні дужки, що заощадить вам години і години та години рефакторингу до чогось моторошно подібного.

Зверніть увагу, що я не розглядаю цей "Функціональний обхід", оскільки я не виконую жодної функції з першого етапу. Це прикордонна лінія "Future Creep", але в якийсь момент ідея про те, що "кодування на завтра сьогодні" - це завжди погано, для мене не викликає хвилювання.


3
Не впевнений, але, я думаю, ви мали на увазі: публічна функція getConnection () {if (! $ This-> db) $ this-> db = new PDO (...); повернути $ this-> db; }
Dycey

Дякую! Чи втратив би я якусь перевагу використання цього методу, використовуючи return self::$factory->getConnection();замість цього return self::$factory;?
Ніко Бернс,

3
Я хотів би використовувати цей код у проекті, який я роблю. Чи можу я цитувати цю сторінку, і якщо так, то під якою ліцензією знаходиться цей текст? Це CC-BY, BSD чи щось інше? Наразі я заявляю це як "Невідомо - повірте суспільному домену", але я хотів би вказати правильні умови ліцензії.
JonTheNiceGuy

2
Я думаю, нам слід зробити "$ db" "$ this-> db" у методі getConnection (), інакше не використовується "private $ db" змінна "", яка ніде офіційно не посилається.
розробник10

Привіт, ваше рішення виглядає круто та масштабовано. Будь ласка, дайте мені знати, що тут потрібно впровадити self :: $ factory = new ConnectionFactory (...);
Ананда

16

Я не впевнений, що можу відповісти на ваше конкретне запитання, але хотів би припустити, що глобальні / одиночні об'єкти з'єднання можуть бути не найкращою ідеєю, якщо це стосується веб-системи. СУБД, як правило, розроблені для ефективного управління великою кількістю унікальних з'єднань. Якщо ви використовуєте глобальний об'єкт підключення, то ви робите кілька речей:

  1. Змушуючи сторінки робити послідовно всі підключення до бази даних і вбиваючи будь-які спроби асинхронного завантаження сторінок.

  2. Потенційно утримування відкритих блокувань елементів бази даних довше, ніж це потрібно, уповільнюючи загальну продуктивність бази даних.

  3. Максимальна загальна кількість одночасних з'єднань, які може підтримувати ваша база даних, і блокує доступ нових користувачів до ресурсів.

Я впевнений, що є й інші потенційні наслідки. Пам’ятайте, що цей метод намагатиметься підтримувати з’єднання з базою даних для кожного користувача, який отримує доступ до сайту. Якщо у вас лише один або два користувачі, це не проблема. Якщо це загальнодоступний веб-сайт, і вам потрібен трафік, то масштабування стане проблемою.

[РЕДАГУВАТИ]

У більш масштабних ситуаціях створення нових підключень кожного разу, коли ви потрапляєте в базу даних, може бути поганим. Однак відповідь полягає не в тому, щоб створити глобальний зв’язок і використовувати його повторно для всього. Відповідь - пул з'єднань.

Завдяки об'єднанню з'єднань підтримується низка різних з'єднань. Коли програма вимагає підключення, перше доступне підключення з пулу отримується, а потім повертається до пулу після завершення його роботи. Якщо запит на з’єднання є недоступним, відбудеться одна з двох речей: а) якщо не досягнуто максимальної кількості дозволених з’єднань, відкривається нове з’єднання, або б) програма змушена чекати, поки з’єднання стане доступним .

Примітка: У мовах .Net об'єднання об'єднань за замовчуванням обробляється об'єктами ADO.Net (рядок з'єднання встановлює всю необхідну інформацію).

Дякую Креду за коментар щодо цього.


Я вважаю, що використання синглтона дає мені перевагу в тому, щоб ініціалізувати підключення якомога пізніше, тоді як, якщо я використовую глобальну, я, мабуть, ініціалізую майже на початку сценарію. Я прав?
Імран,

Правильно, якщо б ви їхали цією дорогою, одиночка забезпечить цю перевагу.
Dr8k

Власне, все залежить від масштабу. У великомасштабних веб-розгортаннях величезна кількість з'єднань БД є злом. Ось чому такі програми, як pgBouncer, існують для PostgreSQL, а Java здійснює об’єднання ресурсів.
Gavin M. Roy

Правда, але я думаю, що правильний пул з'єднань відрізняється від простого використання глобальних об'єктів зв'язку. Пул з'єднань все ще використовує декілька з'єднань, він просто обмежує максимальну кількість і повторно використовує їх з часом, щоб полегшити накладні витрати на налаштування.
Dr8k

5
Зверніть увагу, що "глобальний" у випадку PHP не робить змінну глобальною на сторінках PHP. Це просто означає, що до нього можна отримати доступ із функцій.
Ates Goral,

7

Метод singleton був створений, щоб переконатися, що в будь-якому класі є лише один екземпляр. Але, оскільки люди використовують це як спосіб глобалізації, це стає відомим як ліниве та / або погане програмування.

Тому я б ігнорував глобальний та Singleton, оскільки обидва насправді не є ООП.

Те, що ви шукали, - це введення залежності .

Ви можете перевірити легку для читання інформацію на основі PHP, пов’язану з введенням залежностей (з прикладами) за адресою http://components.symfony-project.org/dependency-injection/trunk/book/01-Dependency-Injection


3

Обидва шаблони досягають однакового чистого ефекту, забезпечуючи єдину точку доступу для дзвінків до бази даних.

З точки зору конкретної реалізації, синглтон має невелику перевагу - не ініціювати підключення до бази даних, доки принаймні один з ваших інших методів цього не вимагає. На практиці в більшості програм, про які я писав, це не має великої різниці, але це потенційна перевага, якщо у вас є кілька сторінок / шляхів виконання, які взагалі не роблять жодних викликів до бази даних, оскільки ці сторінки не ніколи не вимагайте підключення до бази даних.

Ще одна незначна відмінність полягає в тому, що глобальна реалізація може ненавмисно потоптати інші імена змінних у програмі. Навряд чи ви коли-небудь випадково оголосите чергову глобальну посилання на $ db, хоча цілком можливо, що ви можете випадково її перезаписати (скажімо, ви пишете if ($ db = null), коли мали намір писати if ($ db == null). Об'єкт-одиночка запобігає цьому.


2

Якщо ви не збираєтеся використовувати постійне з'єднання, і є випадки, коли цього не робите, я вважаю, що синглтон є концептуально більш приємним, ніж глобальний дизайн ОО.

У справжній архітектурі ОО синглтон є більш ефективним, ніж створення нового екземпляра об'єкта кожного разу.


2

У наведеному прикладі я не бачу підстав використовувати однотонні. Як правило, якщо моя єдина проблема полягає в тому, щоб дозволити один екземпляр об’єкта, якщо мова це дозволяє, я вважаю за краще використовувати глобальні символи


1

Взагалі, я б використовував синглтон для підключення до бази даних ... Ви не хочете створювати нове з'єднання щоразу, коли вам потрібно взаємодіяти з базою даних ... Це може зашкодити продуктивності та пропускній здатності вашої мережі ... Навіщо створювати новий, коли такий є ... Тільки мої 2 центи ...

RWendi


Ініціалізуючи підключення в глобальній області, я ініціалізую підключення один раз на сторінку та використовую цю глобальну змінну у функціях, які повинні взаємодіяти з базою даних.
Імран,

Використання синглтона для підключення до бази даних - це не те саме, що не повторне створення зв’язку при кожній взаємодії зі СУБД. Синглтон - це саме це; один екземпляр даного класу і єдиний, якому дозволено існувати глобально. Можливо, вам доведеться підключатися до різних баз даних одночасно.
Роб

я звертався до синглтон-класу, який управляє підключеннями до бази даних. Я не бачу сенсу створювати новий об'єкт підключення кожного разу, коли ви хочете взаємодіяти з dbms. Звичайно, якщо вам потрібно одночасно підключитися до іншої бази даних, можливо, вам доведеться створити інший об'єкт підключення.
RWendi

0

Це досить просто. Ніколи не використовуйте глобальну АБО Singleton.


4
Коли справа стосується шаблону Сінглтона, завжди говоріть ніколи
1800 ІНФОРМАЦІЯ

3
Що робити, якщо потрібно більше одного постачальника журналів? Хто каже, що я не можу увійти до файлу та консолі?
1800 ІНФОРМАЦІЯ

2
Отже, ви б потім поєднали один анти-шаблон (Singleton) з іншим (God Object)
1800 ІНФОРМАЦІЯ

3
Так. Так само роблять дизайнери всіх основних мов ООП, дозволяючи об’єкти глобального класу. Якщо ви монотеїст і хочете, щоб об’єкт представляв Бога, тоді ви шукаєте об’єкт Бога-одиночки. Все інше є неправильним.
Стів Джессоп,

4
Якби я був монтеїстом і хотів створити об’єкт Бога, я міг би визначити використання шаблону MonotheistAbstractFactory для створення мого об’єкта Бога? Це залишило б відкритою можливість для політеїстичних користувачів також використовувати мою програму, вказавши PolytheistAbstractfactory.
1800 ІНФОРМАЦІЯ

0

Як порада, як singleton, так і global є дійсними і їх можна об’єднати в рамках однієї системи, проекту, плагіна, продукту тощо ... У моєму випадку я роблю цифрові продукти для Інтернету (плагіни).

Я використовую лише синглтон в основному класі і використовую його за принципом. Я майже не використовую його, бо знаю, що основний клас не створить його повторно

<?php // file0.php

final class Main_Class
{
    private static $instance;
    private $time;

    private final function __construct()
    {
        $this->time = 0;
    }
    public final static function getInstance() : self
    {
        if (self::$instance instanceof self) {
            return self::$instance;
        }

        return self::$instance = new self();
    }
    public final function __clone()
    {
        throw new LogicException("Cloning timer is prohibited");
    }
    public final function __sleep()
    {
        throw new LogicException("Serializing timer is prohibited");
    }
    public final function __wakeup()
    {
        throw new LogicException("UnSerializing timer is prohibited");
    }
}

Глобальне використання майже для всіх середніх класів, приклад:

<?php // file1.php
global $YUZO;
$YUZO = new YUZO; // YUZO is name class

в той час як під час виконання я можу використовувати Global для виклику їх методів та атрибутів у тому ж екземплярі, оскільки мені не потрібен інший екземпляр мого основного класу продукту.

<?php // file2.php
global $YUZO;
$YUZO->method1()->run();
$YUZO->method2( 'parameter' )->html()->print();

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

In conclusion:, ви повинні, якщо ви вже добре розумієте, що таке анти-шаблон Singleton і розумієте Global , ви можете використовувати один із 2 варіантів або змішати їх, але якщо я рекомендую не зловживати, оскільки є багато програмістів, які є дуже винятками і вірними програмування ООП, використовуйте його для основного та середнього класів, які ви часто використовуєте протягом часу виконання. (Це значно економить ваш процесор). 😉

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.