Блог


Паттерны в PHP (Singleton)

Начинающие программисты, столкнувшись с ООП, обязтельно натыкаются на необходимость использования (ну или хотя бы понимания) шаблонов проектирования. И это многих пугает. Мало того, что сама парадигма достаточно сложна для понимания, так еще и паттерны какие-то. И хотя они как раз и придуманы для облегчения использования парадигмы, на первых порах играют как раз обратную роль. А всё из-за того, что те, кто придумывают паттерны, давно в ООП как рыба в воде, соответственно и объясняют их со своей колокольни. И получается замкнутый круг. Не зная паттернов сложно грамотно использовать ООП парадигму, однако не зная ООП сложно понять эти самые паттерны.

А все как обычно крайне просто. Нужно только создать необходимую ассоциацию, как все встанет на свои места.

Вот сейчас я попробую объяснить пару шаблонов простым русским языком. Может что-либо и получится.

Начнем с синглтона. Singleton переводится с английского как "одиночка". Но это так, ремарка. Теперь перенесемся в реальную жизнь.

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

А теперь представьте, что вы эту корочку ненароком потеряли. Ну или она пришла в негодность, да мало ли. Вам не нужно снова учиться, не нужно сдавать экзамены. Это уже все есть пожизненно. Вам нужно просто придти в ГАИ и получить дубликат. Тоесть результат когда то произведенных вами действий.

Это и есть суть "синглтон", тоесть "один раз". Необходимые для получения прав действия (обучение и сдача экзаменов)  мы производим один раз в жизни. А результатом можем пользоваться сколь угодно мгого раз.

Теперь дальше, ближе к программированию. Вообще этот паттерн не являются паттерном ООП, как многие ошибочно полагают. Это паттерны проектировния или design patterns. Их можно использовать и помимо ООП и даже классов, в обычных функциях. Вот, чтобы было понятнее, с них и начнем. Вот допустим такая простенькая функция:

1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
<?php

    
function mySingleton()
    {
        
$time microtime(true);
        
        return 
$time;
    }
    
    echo 
mySingleton();
    echo 
'<br>';
    
sleep(1);
    echo 
mySingleton();


Если зпустить этот код, второй результат будет на секунду отличаться от первого. Ну это и понятно, не требует пояснений. Но вот если сделать так:

1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
<?php

    
function mySingleton()
    {
        static 
$time;
        
        if(empty(
$time))
            
$time microtime(true);
        
        return 
$time;
    }
    
    echo 
mySingleton();
    echo 
'<br>';
    
sleep(1);
    echo 
mySingleton();

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

Ну а теперь непосредственно в ООП. Не будем уходить от темы, просто обернем первую функцию в класс:

1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
<?php


class mySingleton
{
    public 
$time;

    public function 
__construct()
    {
        
$this->time microtime(true);
    }
}

    
$obj = new mySingleton();
    echo 
$obj->time;

    echo 
'<br>';
    
sleep(1);

    
$obj = new mySingleton();
    echo 
$obj->time;

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

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

1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26

<?php

class mySingleton
{
    public 
$time;

    public static function 
getInstance() 
    {        
        return new 
self;
    }    
        
    public function 
__construct()
    {
        
$this->time microtime(true);
    }
}

    
$obj mySingleton::getInstance();
    echo 
$obj->time;
    
    echo 
'<br>';
    
sleep(1);
    
    
$obj mySingleton::getInstance();
    echo 
$obj->time;

Обратите внимание, снаружи мы получили объект  без конструкции new, обртившись к статическому методу класса через двойное двоеточие. Однако объектом он от этого быть не перестал. Хотя и результат не изменился. 

Однако мы же знаем, что объект можно легко поместить в переменную. И тем более в свойство. Ну а его можно объявить статичным. И вот мы плавно подошли к реализации синглтона. Делаем по образу и подобию нашей второй функции:

1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
<?php


class mySingleton
{
    public 
$time;
    static private 
$instance;

    public static function 
getInstance() 
    {        
        if(empty(
self::$instance))  
            
self::$instance = new self;

        return 
self::$instance;
    }      
    
    public function 
__construct()
    {
        
$this->time microtime(true);
    }
}

    
$obj mySingleton::getInstance();
    echo 
$obj->time;
    
    echo 
'<br>';
    
sleep(1);
    
    
$obj mySingleton::getInstance();
    echo 
$obj->time;


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

1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
<?php


class mySingleton
{
    public 
$time;
    static private 
$instance;

    public static function 
getInstance() 
    {        
        if(empty(
self::$instance))  
            
self::$instance = new self;
        
        return 
self::$instance;
    }    
        
    private function 
__construct()
    {
        
$this->time microtime(true);
    }
    
    private function 
__clone(){}
}

    
$obj mySingleton::getInstance();
    echo 
$obj->time;
    
    echo 
'<br>';
    
sleep(1);
    
    
$obj mySingleton::getInstance();
    echo 
$obj->time;


Ну вот и все, мы имеем одну из самых распространенных реализаций паттерна Singletone


Что можно добавить. Использовать этот паттерн нужно с большой осторожностью, у него есть несколько недостатков. Он  сродни глобальной переменной, он нарушает принцип SRP ну и так далее. Это тема отдельной публикации.

Но иногда, особенно при упомянутой организации коннекта к серверу СУБД, бывает очень даже полезен.

Просто не забывайте принцип KISS и используйте все в меру.

В следующий раз постараюсь рассказать еще несколько "знаменитых" паттернов, а пока всё)))

Николай aka twin

Теги: Паттерны | PHP

Комментарии (8)

pamparam
30-12-2013
Спасибо...
С новым Годом всех благ
Andrey
08-01-2014
А еще оно нужно, чтоб криворукий админ в ГАИ не смог затереть ваши права в базе (или в журнале), после того как Вы их получили!
Виктор
28-05-2014
Чет для меня паттерны покамись тяжеловаты, хотя очень интересно...
JuNona
05-07-2014
Любое "покамест" проходит.
Слава
21-07-2014
Один момент не совсем ясен, что происходит в этом месте - return new self?
Михал
27-05-2015
В этом месте создаётся экземпляр класса mySingleton, в котором функция и написана. Только там не  return new self, а сначала создаётся экземпляр, если его не было, а потом уже возвращается.

Класс создаёт экземпляр самого себя, хранит его в статическом свойстве $instance и возвращает, при вызове этой функции.
Слава
14-06-2015
Спасибо, как раз кстати), где бы почитать про такие тонкости ООП, что бы все в куче было?

 
Наверх