Цель.
Гарантировать, что класс имеет поименованные экземпляры объекта и обеспечивает глобальную точку доступа к ним. Например, если нужно одновременно иметь несколько подключений к источникам данных. Очень похож на шаблон синглетон, но расширяет его концепцию, сохраняя порождённые объекты в ассоциативном массиве, а не в переменной.
UML-диаграмма
Недостатки, Альтернативы, Резюме.
Аналогичны шаблону синглетон. Так же можно считать антипаттерном.
Реализация на PHP.
class Multiton
{
/** The first instance key. */
const INSTANCE_1 = '1';
/** The second instance key. */
const INSTANCE_2 = '2';
private static $instances = array();
private function __construct() { }
public static function getFirstInstance()
{
return self::getInstance(self::INSTANCE_1);
}
public static function getSecondInstance()
{
return self::getInstance(self::INSTANCE_2);
}
public function doSomething() { }
/**
* @return Multiton
*/
private static function getInstance($instanceName)
{
if (!array_key_exists($instanceName, self::$instances)) {
self::$instances[$instanceName] = new self();
}
return self::$instances[$instanceName];
}
private function __clone() { } // Защищаем от создания через клонирование
private function __wakeup() { } // Защищаем от создания через unserialize
}
// Использование
Multiton::getFirstInstance()->doSomething();
Multiton::getSecondInstance()->doSomething();
Немного доработав этот шаблон можно использовать как базовый класс для всех синглетоновв системе:
abstract class AbstractSingleton
{
private static $instances = array();
protected function __construct() {/*_*/}
/**
* @return static
*/
final public static function me()
{
return self::getInstance(get_called_class());
}
private static function getInstance($class)
{
if (!isset(self::$instances[$class])) {
self::$instances[$class] = new $class;
}
return self::$instances[$class];
}
private function __clone() { } // Защищаем от создания через клонирование
private function __wakeup() { } // Защищаем от создания через unserialize
}
// Использование
class Foo extends AbstractSingleton
{
protected function __construct() { /* инициализация для конкретного наследника, если нужна */ }
public function doSomething() { }
}
class Bar extends AbstractSingleton
{
public function doSomethingOther() { }
}
Foo::me()->doSomething();
Bar::me()->doSomethingOther();Нецелевое использование.
Замена ужасного кода немного лучшим на одном из этапов рефакторинга.
Известный факт: статические методы - зло. Причин на то много и сейчас не об этом. Допустим есть проект, где один из классов использует исключительно статические методы. Пусть для простоты - один метод. Проект растёт и класс этот используется в проекте все в большем и большем количестве мест. То есть, чтобы заменить его статический метод на динамический уже требуются значительные затраты. И вот в один прекрасный день выясняется, что в проекте нужен ещё один (и даже не один) класс с похожим функционалом. В случае с динамическими коассами все просто - общий код выносится в абстрактный класс и от него наследуются классы с необходимыми функциями. Но abstract static function - это нонсенс. Выхода два - или делать почти одинаковые статические классы (ужас), или превратить статические классы в динамические, оставив при этом статический вызов и гарантировать при этом, что обращение будет к единственному экземпляру класса (не зря же исходный класс был статическим - что-то этим хотели сказать авторы или чего-то боялись). В решении подобной проблемы может помочь мультитон.
Код до рефакторинга:
class Foo
{
public static function doSomething()
{
self::doCommonStep();
self::doSpecificStep();
}
private static function doCommonStep() { /* Дублированный код */ }
private static function doSpecificStep() { /* Специфичный для Foo код */ }
}
class Bar
{
public static function doSomething()
{
self::doCommonStep();
self::doSpecificStep();
}
private static function doCommonStep() { /* Дублированный код */ }
private static function doSpecificStep() { /* Специфичный для Bar код */ }
}
// Использование
Foo::doSomething();
Bar::doSomething();Код после рефакторинга:
abstract class Base
{
private static $instances = array();
protected function __construct() {/*_*/}
/**
* @return static
*/
final public static function me()
{
return self::getInstance(get_called_class());
}
private static function getInstance($class)
{
if (!isset(self::$instances[$class])) {
self::$instances[$class] = new $class;
}
return self::$instances[$class];
}
public static function doSomething()
{
static::me()->something();
}
private function something()
{
$this->doCommonStep();
$this->doSpecificStep();
}
private function doCommonStep() { /* Дублированный код */ }
abstract protected function doSpecificStep();
}
class Foo extends Base
{
protected function doSpecificStep() { /* Специфичный для Foo код */ }
}
class Bar extends Base
{
protected function doSpecificStep() { /* Специфичный для Bar код */ }
}
// Использование
Foo::doSomething();
Bar::doSomething();
