Шаблоны проектирования средствами PHP 5.3+ ::Singleton\Factory

Ирокез

бессмертный пони
Команда форума
Партнер клуба
При использовании шаблонов, важно понимать их суть и целесообразность использования того или иного шаблона.
(Очень неплохое описание шаблонов citforum.ru/SE/project/pattern/index.shtml#toc)

1. Одиночка (Singleton)
Наверное это один из самых простых и понятных шаблонов. Суть которого заключается в создании\получении единственного экземпляра класса.
Где это может быть полезно? Очевидно, единственный экземпляр класса БД, кеширование, доступ к какому либо API (к примеру AdWords) и т.д.

Но пойдем немножко дальше и совместим "Одиночку" с шаблоном Factory (этакий гибрид). В классическом понимании, Factory, при каждом вызове создает новый экземпляр класса, но надо ли оно нам?

Необходимо отметить, что применени паттерна не означает невозможность создание нового экземпляра класса.

PHP:
	# Как этим пользоваться, в приведенном примере обратите внимание на ID экземпляров класса dechex(crc32(spl_object_hash($this)))
	
	class MySqlDb extends \Patterns\ISingleton
	{
	        public function __construct($dbHost,$dbUser,$dbPass) { echo '<pre>'.__METHOD__.' #'.dechex(crc32(spl_object_hash($this)))." $dbHost,$dbUser,$dbPass ".PHP_EOL.'</pre>';}
	        public function Connect($DSN) {}
	        public function Query($Sql) { echo '<pre>'.__METHOD__.' #'.dechex(crc32(spl_object_hash($this))).PHP_EOL.'</pre>';}
	        public function Close() {}
	}
	
	/**
	* @var MySqlDb
	*/
	$Dbi = MySqlDb::Instance('localhost','user','test');	
	# OUTPUT: 	MySqlDb::__construct #f4f605e1 localhost,user,test 
	
	$Dbi->Query("SELECT ...."); 
	# OUTPUT: 	MySqlDb::Query #f4f605e1

	$c = new MySqlDb;	
	# OUTPUT: 	MySqlDb::__construct #d46479aa ,, 
	
	MySqlDb::Instance()->Query("UPDATE ..."); 
	# OUTPUT: 	MySqlDb::Query #f4f605e1
	
	$c->Query("DELETE ...");
	# OUTPUT: 	MySqlDb::Query #d46479aa

	\Patterns\ISingleton::Factory('MySqlDb')->Close();
	# OUTPUT: 	MySqlDb::Close #f4f605e1

class Sphinx
{
    public function __construct($dbHost,$dbUser,$dbPass) { echo '<pre>'.__METHOD__.' #'.dechex(crc32(spl_object_hash($this)))." $dbHost,$dbUser,$dbPass ".PHP_EOL.'</pre>';}
    public function Connect($DSN) {echo '<pre>'.__METHOD__.' #'.dechex(crc32(spl_object_hash($this))).PHP_EOL.'</pre>';}
}

# Обратите внимание, что класс Sphinx не наследует шаблон Singleton, но при этом через Factory, мы всегда будем получать единственный экземпляр класса

	\Patterns\ISingleton::Factory('Sphinx','aaa','bbb','ccc');
	# OUTPUT: 	Sphinx::__construct #85315e7b
	
	\Patterns\ISingleton::Factory('Sphinx')->Connect();
	# OUTPUT: 	Sphinx::Connect #85315e7b
Непосредственно сам класс реализующий шаблон одиночки

PHP:
namespace Patterns;

function Exception($Class,$Method,$Message,$Code=0) { throw new \Exception("$Class::$Method ".$Message,$Code);}

abstract class ISingleton {
    static private $_Objects;
    
    static public function Instance() {
	# эта функция появилась с пхп 5.3, и основная ее полезняшка, применимая к Singleton, в том, что она возвращает имя класса вызвавшего статический метод
        $Class = get_called_class();	

	# использование Reflection оправдано в том случае если необходимо при создании нового экземпляра класса передать в конструктор параметры
        !isset(self::$_Objects[$Class]) && ($reflect = new \ReflectionClass($Class)) && (self::$_Objects[$Class] = $reflect->newInstanceArgs(func_get_args()));

        return self::$_Objects[$Class];
    }
    
    static public function Factory($Class) {
	
	# пробуем найти класс, и вызвать все autoload - ры, возможно впринципе и самому делать include-ы классов, но оно надо?
        !class_exists($Class, true) && \Patterns\Exception(__CLASS__, __METHOD__, 'Class not found');

        $args = array_slice(func_get_args(), 1);
        !isset(self::$_Objects[$Class]) && ($reflect = new \ReflectionClass($Class)) && (self::$_Objects[$Class] = $reflect->newInstanceArgs($args));

        return self::$_Objects[$Class];
    }
    
    public function __clone() { trigger_error(__CLASS__.' can not be cloned.', E_USER_ERROR); }
}

PS: Decorator, Adpater .... coming soon
 
Сверху