Назначение.
Гарантировано иметь единственный экземпляр класса в системе и обеспечить глобальную току доступа к нему.
Часто в системе необходимы сущности или сервисы, которые могут существовать только в единственном экземпляре, например, система логирования или подключение к базе данных. В таких случаях необходимо уметь создавать единственный экземпляр некоторого типа, предоставлять к нему доступ извне и запрещать создание нескольких экземпляров того же типа. Такой объект доступен из любого места кода и своим поведением напоминает глобальную переменную.
UML-диаграмма
Достоинства.
- единственный экземпляр данного класса в системе
Недостатки.
- Фактически является глобальной переменной если хранит состояние;
- Нарушает принцип единственной ответственности (SRP). Помимо выполнения своих непосредственных функций еще и контролирует то, что существует в единственном экземпляре;
- Снижает тестируемость клиентского кода поскольку вместо него невозможно подставить mock-объект;
- Повышает связанность кода.
Альтернативы.
Использование шаблонов DependencyInjection или ServiceLocator. Например, фреймворк Symfony позволяет полностью обойтись без синглетона благодаря своей системе DI.
Резюме.
Из-за большого количества недостатков и наличия разумных альтернатив, гарантирующих достижение цели этого шаблона, можно считать антипаттерном и использовать только в крайних случаях.
Реализация на PHP.
class Singleton
{
private static $instance = null; // экземпляр объекта
private function __construct(){ /* ... @return Singleton */ } // Защищаем от создания через new Singleton
/**
* @return Singleton
*/
public static function me() // Возвращает единственный экземпляр класса.
{
if ( is_null(self::$instance) ) {
self::$instance = new self();
}
return self::$instance;
}
public function doSomething() { }
private function __clone() { } // Защищаем от создания через клонирование
private function __wakeup() { } // Защищаем от создания через unserialize
}
// Использование
Singleton::me()->doSomething();
Если в проекте существует несколько синглетонов, то при такой реализации прийдется в каждом из них писать весь вышеприведенный код (кроме метода doSomething(), конечно). Дублирование кода - крайне плохая практика, пусть даже этот код скорее всего меняться никогда не будет. С другой стороны служебные конструкции, реализующие шаблон, в PHP выглядят достаточно громоздко и затрудняют чтение. Так есть ли способ (в PHP) избежать такого дублирования? Начиная с версии 5.4 этот язык получил trait - кусок кода, который можно внедрять в любой объект по своему усмотрению. Насколько хороши треиты в PHP и насколько, где и как стоит их использовать - отдельная тема. Здесь же посмотрим можно ли использовать trait для при реализации синглетонов и насколько такое использование правильно.
В ряде интернет-источников (в том числе и в википедии) приводится пример подобного кода:
trait Singleton
{
static private $instance = null;
private function __construct() { /* ... @return Singleton */ } // Защищаем от создания через new Singleton
public static function me()
{
if ( is_null(self::$instance) ) {
self::$instance = new static();
}
return self::$instance;
}
private function __clone() { /* ... @return Singleton */ } // Защищаем от создания через клонирование
private function __wakeup() { /* ... @return Singleton */ } // Защищаем от создания через unserialize
}
/**
* Class Foo
* @method static Foo me()
*/
class Foo
{
use Singleton;
public function doSomething() { }
}
// Применение
Foo::me()->doSomething();
А теперь попробуем добавить в Foo публичный конструктор:
class BrokenSingleton
{
use Singleton;
public function __construct() { }
public function doSomething() { }
}
(new BrokenSingleton())->doSomething();
Теперь это уже не синглетон. Замечу, что реализация без trait сделать подобное не позволяет. Это достаточно большой минус, чтобы не использовать подобную конструкцию вообще. Есть другой, более надежный и правильный способ избежать дублирования кода - шаблон мультитон. Хотя он и предназначен для другого, PHP благодаря своей нестрогой типизации позволяет использовать его в том числе и для сокращения дублирования кода.
Реализация на Scala.
В Scala есть ключевое слово object, обозначающее, что этот класс будет создан в единственном экземпляре. Поэтому реализация singletone выглядит совсем просто:
object Singleton {}
// Или как объект-компаньон (в одном файле):
class Foo {}
object Foo {}

Этот комментарий был удален администратором блога.
ОтветитьУдалить