Как создать класс Singleton с использованием классов PHP5?
@Andrew Тогда не создавайте второй экземпляр, подключающийся к базе данных. Передайте этот экземпляр туда, где он нужен. Потребность в синглтоне - это запах кода. Подробнее на gooh.posterous.com/singletons-in-php
@ Андрей Нет, не так. Если вам нужно ограничить экземпляр одним экземпляром, не создавайте второй экземпляр. Неверно, что синглтон снижает сложность вашего приложения. Это увеличивает его. Он является глобальным и вводит взаимосвязь, что снижает ремонтопригодность и увеличивает сложность модульных тестов. Это факт, а не мнение. Кроме того, вам не нужно быть уверенным, что ваши разработчики не могут создать второй экземпляр. Перестань присматривать за ними. И нет, интерфейсы - другое дело. Не стесняйтесь приходить в чат и обсуждать это с нами.
@ Андрей Ммммкей. Без обид, но я предлагаю вам получить книгу о качестве программного обеспечения, прежде чем мы продолжим это обсуждение. Синглтоны не упрощают, а усложняют нормальное обслуживание и развитие. На самом деле, все наоборот: именно юнит-тесты упрощают и делают возможным разработку в первую очередь.
@Andrew: Теперь вы предполагаете, что вам нужно только одно соединение с базой данных. Что происходит, когда ваши требования меняются и вам действительно нужно поговорить с двумя серверами баз данных? Не говоря уже о том, что если вы не можете доверять своей команде выполнение действий Правильно, создание синглтона вам ни в коей мере не поможет. Делайте все с самого начала и соберите команду, которой вы можете доверять, и все будет в порядке.
То, что синглтоном злоупотребляли, не означает, что его следует избегать. Не ненавидьте Синглтон. Иногда это прекрасное решение определенной проблемы. Лучше начать спорить, почему мы не должны его использовать, вместо того, чтобы эмоционально пытаться его принизить.






/**
* Singleton class
*
*/
final class UserFactory
{
/**
* Call this method to get singleton
*
* @return UserFactory
*/
public static function Instance()
{
static $inst = null;
if ($inst === null) {
$inst = new UserFactory();
}
return $inst;
}
/**
* Private ctor so nobody else can instantiate it
*
*/
private function __construct()
{
}
}
Использовать:
$fact = UserFactory::Instance();
$fact2 = UserFactory::Instance();
$fact == $fact2;
Но:
$fact = new UserFactory()
Выдает ошибку.
См. http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static, чтобы понять области статических переменных и почему установка static $inst = null; работает.
для сравнения двух экземпляров вы должны использовать ===, а не ==. == вернет истину, если $ fact1 и $ fact2 принадлежат одному классу, но === вернет только истину, если они оба являются одним и тем же экземпляром одного и того же объекта.
метод clone также должен быть закрытым
Разве этот метод не сбрасывает экземпляр UserFactory в значение NULL каждый раз, когда вы вызываете Instance ()? В java переменная $ inst будет частным статическим атрибутом, который не следует сбрасывать снова и снова, иначе вы могли бы также не делать ее синглтоном.
В чем причина объявления $ inst внутри Inside ()?
Разве нам не нужно переопределять методы __clone () и __wakeup (), чтобы вызывать ошибки и останавливать клонирование и десериализацию, если это Singleton?
Да, не должно быть static $ inst = null; быть объявленным в теле класса, а не в методе Instance ()?
Я не понимаю, почему static $ inst = null; "игнорируется" при последующих вызовах. Глядя на код, я бы сказал, что это код, в котором вызывается if ($ inst === null), но это не так. Почему?
Вот хорошее описание того, почему и как объявление переменной как статической в функции работает так, как задумал автор: php.net/manual/en/…
Вы должны использовать $ inst = new self (); не $ inst = new UserFactory (); для тех, кто столкнется с этим позже. +1 за использование встроенной методологии PHP.
Вы забыли сделать приватными и другие магические функции, такие как клонирование, сон и пробуждение.
Так много комментариев с ценными / необходимыми вещами, которые нужно изменить, и все еще без адаптации примера. Код не чистый. Голосовать против.
Вероятно, вам следует добавить частный метод __clone (), чтобы запретить клонирование экземпляра.
private function __clone() {}
Если вы не включите этот метод, возможно следующее:
$inst1=UserFactory::Instance(); // to stick with the example provided above
$inst2=clone $inst1;
теперь $inst1! == $inst2 - это уже не тот экземпляр.
PHP 5.3 позволяет создавать наследуемый класс Singleton с помощью позднего статического связывания:
class Singleton
{
protected static $instance = null;
protected function __construct()
{
//Thou shalt not construct that which is unconstructable!
}
protected function __clone()
{
//Me not like clones! Me smash clones!
}
public static function getInstance()
{
if (!isset(static::$instance)) {
static::$instance = new static;
}
return static::$instance;
}
}
Это решает проблему, заключающуюся в том, что до PHP 5.3 любой класс, расширявший Singleton, создавал экземпляр своего родительского класса вместо своего собственного.
Теперь вы можете:
class Foobar extends Singleton {};
$foo = Foobar::getInstance();
И $ foo будет экземпляром Foobar вместо экземпляра Singleton.
Позднее статическое связывание действительно очень хорошо в php 5.3. Жаль, что я все еще не могу его использовать.
От @ggsonic: "subclass should own its own static var. check this: echo get_class(Foobar::getInstance());echo get_class(Singleton::getInstance());".
Это вообще не работает, просто так получилось, что Foobar был первым классом, который вы построили?
еще есть возможность клонировать ..... "$ a = Singleton :: getInstance (); $ b = unserialize (serialize ($ a)); $ a! == $ b;"
Это не работает, если существует более одного подкласса! $instance находится в синглтоне, а не в подклассе. После создания экземпляра подкласса getInstance () вернет этот экземпляр для всех подклассов.
Я попытался отредактировать это для вас, посмотрим, появится ли оно когда-нибудь;) В основном, хотя ответ выше просто должен предоставить определенную функцию «__sleep ()», которая выдает ошибку. Это решает проблему Адриана с копированием экземпляра. Также необходимо использовать get_called_class () для решения нескольких уровней подклассов.
@clive: Спасибо за это, однако приведенное выше решение не решает проблему, на которую уже ответил mpartel. Если вы расширяете абстрактный класс, который, в свою очередь, расширяет одноэлементный класс, то единственный сохраненный экземпляр - это первый определенный экземпляр. --- Вопрос здесь в том, хотите ли вы использовать класс Singleton (который можно использовать только один раз, необходимо будет дублировать для каждого класса, который вы хотите использовать в качестве Singleton) или абстрактного Singleton, который уменьшает необходимость повторения вашего кода? Если первое, то ответ Inwdr правильный. Если последнее, то это не так.
@clive Я не говорю, что вы ошиблись, удалив мою правку, просто объясняя, что происходит :)
Речь не идет о расширении абстрактного класса. НО с этим и PHP7 вы можете расширить синглтон анонимным классом прямо в unitTest и перезаписать экземпляр static :: $ с имитацией объекта. Вы бы назвали это взломом? Смотри сюда eval.in/935225
Этот метод будет применять синглтоны к любому классу, который вы хотите, все, что вам нужно сделать, это добавить 1 метод к классу, который вы хотите сделать синглтоном, и это сделает это за вас.
Здесь также хранятся объекты в классе «SingleTonBase», поэтому вы можете отлаживать все свои объекты, которые вы использовали в своей системе, рекурсивно просматривая объекты SingleTonBase.
Создайте файл с именем SingletonBase.php и включите его в корень вашего скрипта!
Код
abstract class SingletonBase
{
private static $storage = array();
public static function Singleton($class)
{
if (in_array($class,self::$storage))
{
return self::$storage[$class];
}
return self::$storage[$class] = new $class();
}
public static function storage()
{
return self::$storage;
}
}
Затем для любого класса, который вы хотите создать синглтон, просто добавьте этот небольшой единственный метод.
public static function Singleton()
{
return SingletonBase::Singleton(get_class());
}
Вот небольшой пример:
include 'libraries/SingletonBase.resource.php';
class Database
{
//Add that singleton function.
public static function Singleton()
{
return SingletonBase::Singleton(get_class());
}
public function run()
{
echo 'running...';
}
}
$Database = Database::Singleton();
$Database->run();
И вы можете просто добавить эту одноэлементную функцию в любой имеющийся у вас класс, и она создаст только 1 экземпляр для каждого класса.
ПРИМЕЧАНИЕ. Вы всегда должны делать __construct закрытым, чтобы исключить использование new Class (); экземпляры.
protected static $_instance;
public static function getInstance()
{
if (is_null(self::$_instance))
{
self::$_instance = new self();
}
return self::$_instance;
}
Этот код может применяться к любому классу, не заботясь о его имени.
class Database{
//variable to hold db connection
private $db;
//note we used static variable,beacuse an instance cannot be used to refer this
public static $instance;
//note constructor is private so that classcannot be instantiated
private function __construct(){
//code connect to database
}
//to prevent loop hole in PHP so that the class cannot be cloned
private function __clone() {}
//used static function so that, this can be called from other classes
public static function getInstance(){
if ( !(self::$instance instanceof self) ){
self::$instance = new self();
}
return self::$instance;
}
public function query($sql){
//code to run the query
}
}
Access the method getInstance using
$db = Singleton::getInstance();
$db->query();
Я знаю, что это, вероятно, вызовет ненужную войну пламени, но я вижу, что вам может понадобиться более одного подключения к базе данных, поэтому я бы согласился с тем, что синглтон может быть не лучшим решением для этого ... однако есть другие варианты использования шаблона singleton, которые я считаю чрезвычайно полезными.
Вот пример: я решил развернуть свой собственный MVC и шаблонизатор, потому что мне нужно было что-то действительно легкое. Однако данные, которые я хочу отобразить, содержат много специальных математических символов, таких как ≥ и μ, и что у вас есть ... Данные хранятся как фактический символ UTF-8 в моей базе данных, а не в кодировке предварительно HTML, потому что мое приложение может предоставлять другие форматы, такие как PDF и CSV, в дополнение к HTML. Соответствующее место для форматирования HTML находится внутри шаблона («просмотр», если хотите), который отвечает за отображение этого раздела страницы (фрагмента). Я хочу преобразовать их в соответствующие HTML-объекты, но функция PHP get_html_translation_table () работает не очень быстро. Имеет смысл получить данные один раз и сохранить их в виде массива, сделав их доступными для всех. Вот образец, который я собрал, чтобы проверить скорость. Предположительно, это будет работать независимо от того, были ли другие используемые вами методы (после получения экземпляра) статическими или нет.
class EncodeHTMLEntities {
private static $instance = null;//stores the instance of self
private $r = null;//array of chars elligalbe for replacement
private function __clone(){
}//disable cloning, no reason to clone
private function __construct()
{
$allEntities = get_html_translation_table(HTML_ENTITIES, ENT_NOQUOTES);
$specialEntities = get_html_translation_table(HTML_SPECIALCHARS, ENT_NOQUOTES);
$this->r = array_diff($allEntities, $specialEntities);
}
public static function replace($string)
{
if (!(self::$instance instanceof self) ){
self::$instance = new self();
}
return strtr($string, self::$instance->r);
}
}
//test one million encodings of a string
$start = microtime(true);
for($x=0; $x<1000000; $x++){
$dump = EncodeHTMLEntities::replace("Reference method for diagnosis of CDAD, but clinical usefulness limited due to extended turnaround time (≥96 hrs)");
}
$end = microtime(true);
echo "Run time: ".($end-$start)." seconds using singleton\n";
//now repeat the same without using singleton
$start = microtime(true);
for($x=0; $x<1000000; $x++){
$allEntities = get_html_translation_table(HTML_ENTITIES, ENT_NOQUOTES);
$specialEntities = get_html_translation_table(HTML_SPECIALCHARS, ENT_NOQUOTES);
$r = array_diff($allEntities, $specialEntities);
$dump = strtr("Reference method for diagnosis of CDAD, but clinical usefulness limited due to extended turnaround time (≥96 hrs)", $r);
}
$end = microtime(true);
echo "Run time: ".($end-$start)." seconds without using singleton";
В принципе, я видел типичные результаты вроде этого:
php test.php Run time: 27.842966794968 seconds using singleton Run time: 237.78191494942 seconds without using singleton
Так что, хотя я, конечно, не эксперт, я не вижу более удобного и надежного способа уменьшить накладные расходы на медленные вызовы каких-либо данных, сделав при этом его супер простым (одна строка кода для выполнения того, что вам нужно). Конечно, в моем примере есть только один полезный метод, и поэтому он не лучше, чем глобально определенная функция, но как только у вас будет два метода, вы захотите сгруппировать их вместе, верно? Я далеко от базы?
Кроме того, я предпочитаю примеры, которые действительно что-то ДЕЛАЮТ, так как иногда трудно визуализировать, когда пример включает такие утверждения, как «// сделай что-нибудь здесь полезное», которые я постоянно вижу при поиске учебных пособий.
В любом случае, я хотел бы получить любые отзывы или комментарии о том, почему использование синглтона для этого типа вещей вредно (или слишком сложно).
Вот мой пример, который предоставляет возможность вызывать как $ var = new Singleton (), а также создавать 3 переменных для проверки, создает ли он новый объект:
class Singleton{
private static $data;
function __construct(){
if ($this::$data == null){
$this->makeSingleton();
}
echo "<br/>".$this::$data;
}
private function makeSingleton(){
$this::$data = rand(0, 100);
}
public function change($new_val){
$this::$data = $new_val;
}
public function printme(){
echo "<br/>".$this::$data;
}
}
$a = new Singleton();
$b = new Singleton();
$c = new Singleton();
$a->change(-2);
$a->printme();
$b->printme();
$d = new Singleton();
$d->printme();
За исключением того, что это не синглтон. Вы можете создать несколько экземпляров класса Singleton.
Я думаю, что это так, потому что независимо от того, какой экземпляр влияет на класс Singleton, изменения относятся ко всем экземплярам Singleton. Выше я добавил еще две функции. Теперь давайте попробуем изменить данные в одном экземпляре и проверить остальные. Так разве это не синглтон, а если нет - что не так?
Синглтон - это класс, допускающий только один экземпляр самого себя. Создавая несколько экземпляров, вы отменяете этот принцип.
К сожалению, Ответ Inwdr ломается, когда есть несколько подклассов.
Вот правильный наследуемый базовый класс Singleton.
class Singleton
{
private static $instances = array();
protected function __construct() {}
protected function __clone() {}
public function __wakeup()
{
throw new Exception("Cannot unserialize singleton");
}
public static function getInstance()
{
$cls = get_called_class(); // late-static-bound class name
if (!isset(self::$instances[$cls])) {
self::$instances[$cls] = new static;
}
return self::$instances[$cls];
}
}
Код теста:
class Foo extends Singleton {}
class Bar extends Singleton {}
echo get_class(Foo::getInstance()) . "\n";
echo get_class(Bar::getInstance()) . "\n";
Это пока что наиболее близко к правильной реализации синглтона. Вам также следует рассмотреть возможность использования метода __вставай() для предотвращения десериализации.
На самом деле вам нужно либо выбросить исключение, либо вызвать ошибку вручную - объявление функции как защищенной / закрытой вызовет только E_WARNING, говорящее о том, что она не может получить доступ к методу, но в противном случае продолжит работу.
Спасибо. Обычно все предупреждения и т. д. Превращаются в исключения, поэтому я забыл о разнице, когда тестировал: P
Это единственное найденное мной решение, которое правильно работает с несколькими подклассами. Спасибо!
<?php
/**
* Singleton patter in php
**/
trait SingletonTrait {
protected static $inst = null;
/**
* call this method to get instance
**/
public static function getInstance(){
if (static::$inst === null){
static::$inst = new static();
}
return static::$inst;
}
/**
* protected to prevent clonning
**/
protected function __clone(){
}
/**
* protected so no one else can instance it
**/
protected function __construct(){
}
}
использовать:
/**
* example of class definitions using SingletonTrait
*/
class DBFactory {
/**
* we are adding the trait here
**/
use SingletonTrait;
/**
* This class will have a single db connection as an example
**/
protected $db;
/**
* as an example we will create a PDO connection
**/
protected function __construct(){
$this->db =
new PDO('mysql:dbname=foodb;port=3305;host=127.0.0.1','foouser','foopass');
}
}
class DBFactoryChild extends DBFactory {
/**
* we repeating the inst so that it will differentiate it
* from UserFactory singleton
**/
protected static $inst = null;
}
/**
* example of instanciating the classes
*/
$uf0 = DBFactoryChild::getInstance();
var_dump($uf0);
$uf1 = DBFactory::getInstance();
var_dump($uf1);
echo $uf0 === $uf1;
откликнуться:
object(DBFactoryChild)#1 (0) {
}
object(DBFactory)#2 (0) {
}
Если вы используете PHP 5.4: черта, это опция, поэтому вам не нужно тратить впустую иерархию наследования, чтобы иметь Шаблон синглтона
а также обратите внимание, используете ли вы класс черты или расширяет Singleton один свободный конец заключался в создании синглтона дочерних классов, если вы не добавляете следующую строку кода:
protected static $inst = null;
в детском классе
неожиданный результат будет:
object(DBFactoryChild)#1 (0) {
}
object(DBFactoryChild)#1 (0) {
}
Вся эта сложность («позднее статическое связывание» ... harumph) для меня просто признак поломки модели объект / класс PHP. Если бы объекты класса были объектами первого класса (см. Python), тогда "$ _instance" была бы переменной класса пример - членом объекта класса, в отличие от члена / свойства его экземпляров, а также в отличие от общего его потомками. В мире Smalltalk это разница между «переменной класса» и «переменной экземпляра класса».
В PHP мне кажется, что мы должны принять близко к сердцу руководство о том, что шаблоны являются руководством для написания кода - мы, возможно, могли бы подумать о шаблоне Singleton, но пытаемся написать код, который наследуется от реального класса Singleton. выглядит ошибочным для PHP (хотя я предполагал, что какая-то предприимчивая душа могла бы создать подходящее ключевое слово SVN).
Я продолжу просто кодировать каждый синглтон отдельно, используя общий шаблон.
Заметьте, что я абсолютно остаюсь ВНЕ разговоров о том, что одиночки - зло, жизнь слишком коротка.
Ваши замечания верны при рассмотрении постоянно растущей сложности языка PHP. Похоже, что добавляется слишком много новых ключевых слов, чтобы найти выход из слишком большого количества различных дыр в дизайне в слишком большом количестве различных парадигм кодирования. Хуже того, из-за высокой скорости изменений и перекоса версий между хостами и платформами разработки сегодняшнее «дневное решение» (например, черты в ответе @Eric Anderson [stackoverflow.com/a/23998306/3696363],) не работает в производственных системах, которые могут работать на «стабильной» «версия» вместо «самая последняя, самая лучшая».
Я согласен с первым ответом, но я бы также объявил класс окончательным, чтобы его нельзя было расширить, поскольку расширение синглтона нарушает шаблон синглтона. Также переменная экземпляра должна быть частной, чтобы к ней нельзя было получить прямой доступ. Также сделайте метод __clone закрытым, чтобы вы не могли клонировать одноэлементный объект.
Ниже приведен пример кода.
/**
* Singleton class
*
*/
final class UserFactory
{
private static $_instance = null;
/**
* Private constructor
*
*/
private function __construct() {}
/**
* Private clone method
*
*/
private function __clone() {}
/**
* Call this method to get singleton
*
* @return UserFactory
*/
public static function getInstance()
{
if (self::$_instance === null) {
self::$_instance = new UserFactory();
}
return self::$_instance;
}
}
Пример использования
$user_factory = UserFactory::getInstance();
Что это мешает вам делать (что нарушит шаблон синглтона ..
ТЫ НЕ СМОЖЕШЬ ЭТО СДЕЛАТЬ!
$user_factory = UserFactory::$_instance;
class SecondUserFactory extends UserFactory { }
Это должен быть правильный путь Синглтона.
class Singleton {
private static $instance;
private $count = 0;
protected function __construct(){
}
public static function singleton(){
if (!isset(self::$instance)) {
self::$instance = new Singleton;
}
return self::$instance;
}
public function increment()
{
return $this->count++;
}
protected function __clone(){
}
protected function __wakeup(){
}
}
Мне понравился метод использования трейтов @ jose-segura, но мне не понравилась необходимость определять статическую переменную для подклассов. Ниже приведено решение, позволяющее избежать этого путем кэширования экземпляров в статической локальной переменной в метод фабрики, индексированный по имени класса:
<?php
trait Singleton {
# Single point of entry for creating a new instance. For a given
# class always returns the same instance.
public static function instance(){
static $instances = array();
$class = get_called_class();
if ( !isset($instances[$class]) ) $instances[$class] = new $class();
return $instances[$class];
}
# Kill traditional methods of creating new instances
protected function __clone() {}
protected function __construct() {}
}
Использование такое же, как и @ jose-segura, только статическая переменная в подклассах не требуется.
Класс базы данных, который проверяет, существует ли какой-либо существующий экземпляр базы данных, он вернет предыдущий экземпляр.
class Database {
public static $instance;
public static function getInstance(){
if (!isset(Database::$instance) ) {
Database::$instance = new Database();
}
return Database::$instance;
}
private function __cunstruct() {
/* private and cant create multiple objects */
}
public function getQuery(){
return "Test Query Data";
}
}
$dbObj = Database::getInstance();
$dbObj2 = Database::getInstance();
var_dump($dbObj);
var_dump($dbObj2);
/*
After execution you will get following output:
object(Database)[1]
object(Database)[1]
*/
Ссылка http://www.phptechi.com/php-singleton-design-patterns-example.html
В этой статье довольно подробно освещена тема: http://www.phptherightway.com/pages/Design-Patterns.html#singleton
Note the following:
- The constructor
__construct()is declared asprotectedto prevent creating a new instance outside of the class via thenewoperator.- The magic method
__clone()is declared asprivateto prevent cloning of an instance of the class via thecloneoperator.- The magic method
__wakeup()is declared asprivateto prevent unserializing of an instance of the class via the global functionunserialize().- A new instance is created via late static binding in the static creation method
getInstance()with the keywordstatic. This allows the subclassing of theclass Singletonin the example.
Это пример создания синглтона в классе базы данных.
шаблоны проектирования 1) singleton (синглтон)
class Database{
public static $instance;
public static function getInstance(){
if (!isset(Database::$instance)){
Database::$instance=new Database();
return Database::$instance;
}
}
$db=Database::getInstance();
$db2=Database::getInstance();
$db3=Database::getInstance();
var_dump($db);
var_dump($db2);
var_dump($db3);
тогда выход -
object(Database)[1]
object(Database)[1]
object(Database)[1]
использовать только один экземпляр, а не создавать 3 экземпляра
Настоящий и современный способ сделать шаблон Singleton:
<?php
/**
* Singleton Pattern.
*
* Modern implementation.
*/
class Singleton
{
/**
* Call this method to get singleton
*/
public static function instance()
{
static $instance = false;
if ( $instance === false )
{
// Late static binding (PHP 5.3+)
$instance = new static();
}
return $instance;
}
/**
* Make constructor private, so nobody can call "new Class".
*/
private function __construct() {}
/**
* Make clone magic method private, so nobody can clone instance.
*/
private function __clone() {}
/**
* Make sleep magic method private, so nobody can serialize instance.
*/
private function __sleep() {}
/**
* Make wakeup magic method private, so nobody can unserialize instance.
*/
private function __wakeup() {}
}
Так что теперь вы можете использовать это как.
<?php
/**
* Database.
*
* Inherited from Singleton, so it's now got singleton behavior.
*/
class Database extends Singleton {
protected $label;
/**
* Example of that singleton is working correctly.
*/
public function setLabel($label)
{
$this->label = $label;
}
public function getLabel()
{
return $this->label;
}
}
// create first instance
$database = Database::instance();
$database->setLabel('Abraham');
echo $database->getLabel() . PHP_EOL;
// now try to create other instance as well
$other_db = Database::instance();
echo $other_db->getLabel() . PHP_EOL; // Abraham
$other_db->setLabel('Priler');
echo $database->getLabel() . PHP_EOL; // Priler
echo $other_db->getLabel() . PHP_EOL; // Priler
Как видите, эта реализация намного более гибкая.
Это самый ясный ответ о шаблоне Singleton в этой теме. Спасибо.
Я реализовал этот подход, и он работает как указано: второй экземпляр становится нулевым. Однако мне не потребовалось также расширять конкретный класс. Я только что реализовал Singleton :: instance () в конструкторе этого конкретного класса.
в функции instance$instance должен быть null, а не false
Да, но это не функция, а метод.
Вам действительно не нужно использовать шаблон Singleton, потому что он считается антипаттерном. В общем, есть много причин не реализовывать этот шаблон вообще. Прочтите это, чтобы начать с: Лучшая практика для одноэлементных классов PHP.
Если в конце концов вы все еще думаете, что вам нужно использовать шаблон Singleton, мы могли бы написать класс, который позволит нам получить функциональность Singleton, расширив наш абстрактный класс SingletonClassVendor.
Это то, с чем я пришел, чтобы решить эту проблему.
<?php
namespace wl;
/**
* @author DevWL
* @dosc allows only one instance for each extending class.
* it acts a litle bit as registry from the SingletonClassVendor abstract class point of view
* but it provides a valid singleton behaviour for its children classes
* Be aware, the singleton pattern is consider to be an anti-pattern
* mostly because it can be hard to debug and it comes with some limitations.
* In most cases you do not need to use singleton pattern
* so take a longer moment to think about it before you use it.
*/
abstract class SingletonClassVendor
{
/**
* holds an single instance of the child class
*
* @var array of objects
*/
protected static $instance = [];
/**
* @desc provides a single slot to hold an instance interchanble between all child classes.
* @return object
*/
public static final function getInstance(){
$class = get_called_class(); // or get_class(new static());
if (!isset(self::$instance[$class]) || !self::$instance[$class] instanceof $class){
self::$instance[$class] = new static(); // create and instance of child class which extends Singleton super class
echo "new ". $class . PHP_EOL; // remove this line after testing
return self::$instance[$class]; // remove this line after testing
}
echo "old ". $class . PHP_EOL; // remove this line after testing
return static::$instance[$class];
}
/**
* Make constructor abstract to force protected implementation of the __constructor() method, so that nobody can call directly "new Class()".
*/
abstract protected function __construct();
/**
* Make clone magic method private, so nobody can clone instance.
*/
private function __clone() {}
/**
* Make sleep magic method private, so nobody can serialize instance.
*/
private function __sleep() {}
/**
* Make wakeup magic method private, so nobody can unserialize instance.
*/
private function __wakeup() {}
}
Пример использования:
/**
* EXAMPLE
*/
/**
* @example 1 - Database class by extending SingletonClassVendor abstract class becomes fully functional singleton
* __constructor must be set to protected becaouse:
* 1 to allow instansiation from parent class
* 2 to prevent direct instanciation of object with "new" keword.
* 3 to meet requierments of SingletonClassVendor abstract class
*/
class Database extends SingletonClassVendor
{
public $type = "SomeClass";
protected function __construct(){
echo "DDDDDDDDD". PHP_EOL; // remove this line after testing
}
}
/**
* @example 2 - Config ...
*/
class Config extends SingletonClassVendor
{
public $name = "Config";
protected function __construct(){
echo "CCCCCCCCCC" . PHP_EOL; // remove this line after testing
}
}
Просто чтобы доказать, что это работает так, как ожидалось:
/**
* TESTING
*/
$bd1 = Database::getInstance(); // new
$bd2 = Database::getInstance(); // old
$bd3 = Config::getInstance(); // new
$bd4 = Config::getInstance(); // old
$bd5 = Config::getInstance(); // old
$bd6 = Database::getInstance(); // old
$bd7 = Database::getInstance(); // old
$bd8 = Config::getInstance(); // old
echo PHP_EOL."COMPARE ALL DATABASE INSTANCES".PHP_EOL;
var_dump($bd1);
echo '$bd1 === $bd2' . ($bd1 === $bd2)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
echo '$bd2 === $bd6' . ($bd2 === $bd6)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
echo '$bd6 === $bd7' . ($bd6 === $bd7)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
echo PHP_EOL;
echo PHP_EOL."COMPARE ALL CONFIG INSTANCES". PHP_EOL;
var_dump($bd3);
echo '$bd3 === $bd4' . ($bd3 === $bd4)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
echo '$bd4 === $bd5' . ($bd4 === $bd5)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
echo '$bd5 === $bd8' . ($bd5 === $bd8)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
Читая больше ответов, я имел в виду что-то вроде этого. К счастью, он уже был здесь :)
Я давно написал, думал поделиться здесь
class SingletonDesignPattern {
//just for demo there will be only one instance
private static $instanceCount =0;
//create the private instance variable
private static $myInstance=null;
//make constructor private so no one create object using new Keyword
private function __construct(){}
//no one clone the object
private function __clone(){}
//avoid serialazation
public function __wakeup(){}
//ony one way to create object
public static function getInstance(){
if (self::$myInstance==null){
self::$myInstance=new SingletonDesignPattern();
self::$instanceCount++;
}
return self::$myInstance;
}
public static function getInstanceCount(){
return self::$instanceCount;
}
}
//now lets play with singleton design pattern
$instance = SingletonDesignPattern::getInstance();
$instance = SingletonDesignPattern::getInstance();
$instance = SingletonDesignPattern::getInstance();
$instance = SingletonDesignPattern::getInstance();
echo "number of instances: ".SingletonDesignPattern::getInstanceCount();
Быстрый пример:
final class Singleton
{
private static $instance = null;
private function __construct(){}
private function __clone(){}
private function __wakeup(){}
public static function get_instance()
{
if ( static::$instance === null ) {
static::$instance = new static();
}
return static::$instance;
}
}
Надеюсь на помощь.