Если вы взялись всерьёз программировать на ООП, готовьтесь к тому, что тут на каждом шагу грабли и вилы. Код более сложный и менее управляемый в силу своей запутанности, это приводит к куче логических ошибок, которые диагностировать очень трудно. Для того, чтобы избежать подобного рода недоразумений, а так же для того, чтобы как то упорядочить этот запутанный код, придуманы так называемые паттерны. Тоесть по русски - костыли, которые помогают не рухнуть сооружению, если оно строится несколькими подрядчиками или неумелыми гастарбайтерами.
Синглетон (Singleton)
Самым ярким и академическим примером является работа с базой данных.
Вот допустим у нас имеется класс для работы с БД.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 |
<?php
class dataBase {
public function __construct() { echo 'Тут как бы коннект к базе<br>'; /* mysql_connect( DBSERVER, DBUSER, DBPASSWORD ); mysql_select_db( DATABASE ); */ } public function mysqlAssoc($query) { echo 'А тут как бы запрос: '. $query; /* $res = mysql_query($query); return mysql_fetch_assoc($res); */ } }
$db = new dataBase(); $db -> mysqlAssoc("SELECT * FROM `table`");
|
Все достаточно пристойно, как водится, в конструкторе осуществляется подключение (коннект) к серверу баз данных, дальше работаем с нужными методами.
Однако если разработчиков несколько, либо имеются провалы в памяти, не исключена ситуация, что объект будет инициализирован несколько раз в разных местах. Там, где потребуется работа с базой данных. Это приведет к тому, что конструктор тоже сработает несколько раз. А значит и соединений с сервером баз данных получится несколько, а это лишние телодвижения, перерасход памяти и неоправданные тормоза:
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
35
36
37
38
39 |
<?php
class dataBase {
public function __construct() { echo 'Тут как бы коннект к базе<br>'; /* mysql_connect( DBSERVER, DBUSER, DBPASSWORD ); mysql_select_db( DATABASE ); */ } public function mysqlAssoc($query) { echo 'А тут как бы запрос: '. $query .'<br>'; /* $res = mysql_query($query); return mysql_fetch_assoc($res); */ } }
$db = new dataBase(); $db -> mysqlAssoc("SELECT * FROM `table`"); $db = new dataBase(); $db -> mysqlAssoc("SELECT * FROM `table`"); $db = new dataBase(); $db -> mysqlAssoc("SELECT * FROM `table`"); /* РЕЗУЛЬТАТ Тут как бы коннект к базе А тут как бы запрос: SELECT * FROM `table` Тут как бы коннект к базе А тут как бы запрос: SELECT * FROM `table` Тут как бы коннект к базе А тут как бы запрос: SELECT * FROM `table` */
|
Чтобы этого избежать, придуман хитрый способ - паттерн
синглетон (Singleton). В переводе с английского - одиночка.
Вот мы в класс поместим статическую переменную
$instance, а в неё метод
getInstance() засунет экземпляр нашего класса.
Теперь этот экземпляр можно получить двумя способами, классическим и из статической переменной:
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 |
<?php
class dataBase { /* * В эту переменную мы поместим единственный * экземпляр класса, с которым и будем работать дальше. */ static private $instance;
/* * А это метод, который и осуществит сие действо */ public static function getInstance() { self::$instance = new self; return self::$instance; } /* * Ну а тут прочая белибирда */ public function __construct() { echo 'Тут как бы коннект к базе<br>'; /* mysql_connect( DBSERVER, DBUSER, DBPASSWORD ); mysql_select_db( DATABASE ); */ } public function mysqlAssoc($query) { echo 'А тут как бы запрос: '. $query .'<br>'; /* $res = mysql_query($query); return mysql_fetch_assoc($res); */ } }
// Теперь объект можно получить так: $db = new dataBase(); $db -> mysqlAssoc("SELECT * FROM `table`");
// Или таким извратным способом: $db = dataBase::getInstance();; $db -> mysqlAssoc("SELECT * FROM `table`"); /* РЕЗУЛЬТАТ Тут как бы коннект к базе А тут как бы запрос: SELECT * FROM `table` Тут как бы коннект к базе А тут как бы запрос: SELECT * FROM `table` */
|
Обратите внимание, что инициализировали мы его ключевым словом
self
1
2
3
4
5
6
7
8 |
//Это self::$instance = new self;
//тоже самое, что это self::$instance = new dataBase();
|
На самом деле это не придает читабельности, дело на любителя. Просто нужно знать, что так можно.
Но вернемся к овину с нашими баранами. Для чего нужен такой выкрутас? А вот для чего. Если мы теперь в классе проверим на пустоту переменную
$instance, то сможем узнать, вызывался ли этот класс методом
getInstance(). И если уже вызывался, сделать оргвыводы и не дать вызвать его еще раз, выдав экземпляр из этой переменной.
А чтобы не дать вызвать класс обычным способом, объявим коструктор приватным. Тоесть запретим доступ к нему снаружи. Хватит того, что мы внутри класса уже набарагозили. Ну и таким же способом запретим клонирование на всякий случай.
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 |
<?php
class dataBase { /* * В эту переменную мы поместим единственный * экземпляр класса, с которым и будем работать дальше. */ static private $instance;
/* * А это метод, который и осуществит сие действо * за одно проверив существование объекта. */ public static function getInstance() { if(empty(self::$instance)) self::$instance = new self; return self::$instance; } /* * Ну а тут прочая белибирда с приватным конструктором */ private function __construct() { echo 'Тут как бы коннект к базе<br>'; /* mysql_connect( DBSERVER, DBUSER, DBPASSWORD ); mysql_select_db( DATABASE ); */ } public function mysqlAssoc($query) { echo 'А тут как бы запрос: '. $query .'<br>'; /* $res = mysql_query($query); return mysql_fetch_assoc($res); */ } private function __clone() { /* Фиг вам. */ } }
// Теперь получить объект можно только извратным способом:
$db = dataBase::getInstance();; $db -> mysqlAssoc("SELECT * FROM `table`"); // Несколько раз подряд $db = dataBase::getInstance();; $db -> mysqlAssoc("SELECT * FROM `table`"); // А тут будет ошибка $db = new dataBase(); $db -> mysqlAssoc("SELECT * FROM `table`"); /* РЕЗУЛЬТАТ Тут как бы коннект к базе А тут как бы запрос: SELECT * FROM `table` А тут как бы запрос: SELECT * FROM `table`
Fatal error: Call to private dataBase::__construct() from invalid context */
|
Теперь сколько раз мы бы не получали объект в разных местах (других классах), соединение с базой будет произведено единожды.
Вот и весь синглетон. Теперь в любом месте мы можем получить единственный экземпляр класса. Он как бы попал в глобальное пространство. Это убережет от повторных инициализаций и пересечений, вызвав фатальную ошибку при попытке это совершить.