Цель.
Гарантировать, что класс имеет поименованные экземпляры объекта и обеспечивает глобальную точку доступа к ним. Например, если нужно одновременно иметь несколько подключений к источникам данных. Очень похож на шаблон синглетон, но расширяет его концепцию, сохраняя порождённые объекты в ассоциативном массиве, а не в переменной.
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();
Комментариев нет:
Отправить комментарий