11.02 2011

Flyweight

Перед прочтением ознакомьтесь с введением в паттерны проектирования на PHP, в котором описаны принятые соглашения и понятия. Данная статья дополняется с некоторой периодичностью, так что если вы ее читали ранее, не факт что данные не изменились.

Приспособленец (Flyweight) относиться к классу структурных паттернов. Он используется для эффективной поддержки множества мелких объектов.

В некоторых приложения использование множества мелких объектов могло бы оказаться весьма полезным, однако прямая реализация ведет к чудовищному перерасходу ресурсов. Попробую объяснить это на примере интернет магазина, в котором мы будем продавать крутые дизайнерские подушки. С покупателями у нас не возникает проблем (так как подушки крутые), и список продаж все растет и растет. А так как это тешит наше самолюбие, вводить постраничность мы не спешим. И вот наступает тот момент, когда страница грузится  достаточно долго, чтобы мы успели не только заскучать, но и совершить вечерний променад. Мы достигаем точки кипения и с большой неохотой пытаемся оптимизировать нашу архитектуру. Она из себя представляет собой набор объектов-продаж, включающего объект-покупатель и набор объектов-товаров. Где каждый объект-товар это совокупность таких параметров, как: артикул подушки, цвет подушки, цена, скидка.

Получается, что на каждую запись о продаже инстанцируется минимум 2 объекта (в зависимости от количества купленных подушек). В текущей реализации, порождение такого количества объектов излишне. Путем не сложных манипуляций можно объединить объект-покупатель и объект-товар в один объект продажа. На этом можно не останавливаться и свести все к одному объекту продажи.

Внося такие изменения мы не только теряем в гибкости и расширяемости, но и можем погрязнуть в устранении зависимостей. Решением этой проблемы может стать паттерн приспособленец.

Паттерн приспособленец позволяет повторно использовать мелкие объекты в различном контексте. В итоге мы сможем использовать в различных объектах-продажах одни и теже объекты-покупатели и объекты-товары. Как это работает на примере объекта-товара. У нас есть набор параметров (артикул подушки, цвет подушки, цена, скидка), которые полностью характеризуют этот товар. Соответственно, если у нас было несколько покупателей, которые выбрали одну и туже модель подушки, одного и того же цвета и попали под одну и туже скидку, то итоговые объекты-товары будут полностью идентичны. В итоге мы можем использовать один и тот же объект сразу в нескольких местах, что позволит сэкономить нам немного памяти. При достаточно большом количестве мелких объектов с малым количеством различающихся признаков, мы сможем получить весьма существенную экономию ресурсов. Итак, как структурно выглядит паттерн:

Flyweight (Приспособленец)

Где:

FlyweightFactory - модифицированный паттерн фабрика, для создания приспособленцев. Методу getFlyweight передаются признаки, по которым будет создан новый, либо найден и возвращен уже готовый объект.

Flyweight - абстрактный класс приспособленцев

ConcreteFlyweight - конкретная реализация приспособленца, которая будет замещать собой одинаковые мелкие объекты.

UnsharedFlyweight - реализация приспособленца, который не может быть разделен.

class FlyweightFactory
{
    protected static $_flyweigths = array();

    /**
     * @param string $key
     * @return Flyweight
     */

    public static function getFlyweight($key)
    {
        // key может быть не только строкой, но и любым другим типом,
        // в таком случае необходим иной способ поиска созданных приспособленцев

        if (! isset(self::$_flyweigths[$key])) {
            // здесь могут быть условия, когда создавать обычного приспособленца,
            // а когда возвращать неделимого

            self::$_flyweigths[$key] = new ConcreteFlyweight();
        }

        return self::$_flyweigths[$key];
    }
}

abstract class Flyweight
{
    /**
     * @var mixed внутреннее состояние
     */

    protected $_intrinsicState = null;

    /**
     * @param mixed $extrinsicState
     *  внешнее состояние, передаваемое в приспособленец (контекст)
     */

    public function Operation($extrinsicState)
    {
        //....
    }
}

class ConcreteFlyweight extends Flyweight {}
class UnsharedFlyweight extends Flyweight {}

Скачать исходный код можно тут.

Паттерн приспособленец зачастую используется вместе c паттерном компоновщик, для реализации иерархической структуры в виде ациклического направленного графа с разделяемыми листовыми вершинами.

comments powered by Disqus