PHP Flyweight Design Pattern Структурный паттерн

Паттерн Приспособленец (Flyweight) в PHP

Короткий скринкаст: паттерн «Приспособленец» (Flyweight) — разделяем состояние с помощью кэша. Нарисуем лес: тип дерева один на всех, позиции хранятся отдельно.

Flyweight Pattern — открыть страницу урока

Описание

Приспособленец экономит память, когда объектов много, а повторяющееся состояние мало. Тип дерева (имя, цвет, текстура) не меняется — фабрика возвращает один объект TreeType на всех. Позиция (x, y) уникальна для каждого дерева и живёт в контексте Tree. Посадили три дерева, а тип создали только два.

UML-диаграмма

classDiagram class TreeType { -name: string -color: string -texture: string +render(x: int, y: int): string } class Tree { -x: int -y: int -type: TreeType +draw(): string } class TreeFactory { -types: array +place(x: int, y: int, name: string, color: string, texture: string): Tree +count(): int } TreeFactory o-- TreeType : caches Tree o-- TreeType : shared TreeFactory ..> Tree : creates

Код

TreeType.php

class TreeType
{
    public function __construct(
        public string $name,
        public string $color,
        public string $texture,
    ) {
    }

    public function render(int $x, int $y): string
    {
        return "$this->name at $x, $y";
    }
}

Tree.php

class Tree
{
    public function __construct(
        private int $x,
        private int $y,
        private TreeType $type,
    ) {
    }

    public function draw(): string
    {
        return $this->type->render($this->x, $this->y);
    }
}

TreeFactory.php

class TreeFactory
{
    private array $types = [];

    public function place(int $x, int $y, string $name, string $color, string $texture): Tree
    {
        $key = "$name|$color|$texture";

        if (! isset($this->types[$key])) {
            $this->types[$key] = new TreeType($name, $color, $texture);
        }

        return new Tree($x, $y, $this->types[$key]);
    }

    public function count(): int
    {
        return count($this->types);
    }
}

demo.php

require_once 'TreeType.php';
require_once 'Tree.php';
require_once 'TreeFactory.php';

$factory = new TreeFactory();

$trees[] = $factory->place(0, 0, 'Birch', 'Green', 'leaf.png');
$trees[] = $factory->place(1, 4, 'Birch', 'Green', 'leaf.png');
$trees[] = $factory->place(5, 2, 'Oak', 'DarkGreen', 'oak.png');

echo 'trees: '.count($trees).PHP_EOL;
echo 'types: '.$factory->count().PHP_EOL;

Пример результата

После php demo.php видно, что деревьев три, а типов два — берёзы разделяют один объект TreeType:

trees: 3
types: 2

Преимущества

  • Резко снижает потребление памяти при множестве похожих объектов.
  • Общее (внутреннее) состояние хранится один раз и разделяется.
  • Дорогие разделяемые данные создаются лениво и кэшируются.
  • Клиент продолжает работать с обычными объектами.

Когда использовать

  • Когда объектов очень много и память критична.
  • Когда большую часть состояния можно вынести наружу как внешнее.
  • Когда одинаковые данные повторяются у множества объектов.
  • Когда уникальность каждого экземпляра не требуется.

Использование в Laravel

В Laravel Приспособленец — это кэширование дорогих общих данных: скомпилированные Blade-шаблоны (Illuminate\View\Compilers\BladeCompiler) и загруженные переводы (Illuminate\Translation\Translator) создаются один раз и переиспользуются. Конфиг и роуты, собранные командами config:cache и route:cache, — то же общее «внутреннее» состояние, которое разделяется между запросами вместо повторной сборки.