Интересные разделы:

Шаблон Prototype

Обнавлено: 02.02.2020

В этой статье подробно описан Шаблон Prototype. Прочитав данную статью вы сможете эфектвно использовать данный патернн в своих проектах

Если используется шаблон Factory, возникновение параллельных иерархий наследования может привести к осложнениям. Такое тесное соединение делает некоторых программистов неудобными. Каждый раз, когда вводится новое семейство продуктов, вам нужно создать конкретного производителя. В быстро растущей системе со многими продуктами поддержание этого типа связи может быстро стать очень утомительной проблемой. Чтобы устранить этот тип зависимости, вы можете использовать ключевое слово PHP clone, в частности, для дублирования некоторых существующих продуктов. И тогда определенные классы продуктов становятся основой для создания ваших собственных объектов. По сути, это означает, что используется паттерн Prototype, в результате чего наследование может быть заменено композицией. Этот подход, в свою очередь, способствует гибкости в выполнении программы и уменьшает количество создаваемых классов.

Задача

Представьте себе интернет-игру цивилизационного типа, в которой элементы работают на клеточном поле. Каждая клетка может представлять море, равнину или лес. Тип местности может ограничивать движение и способность элементов занимать ячейку. Предположим, у нас есть объект TerrainFactory (Фабрика области), который позволяет нам создавать Море (море), Лес (лес) и Равнины (равнины). Мы решили дать пользователю возможность выбирать совершенно разные типы сред. Следовательно, объект Sea относится к абстрактному суперклассу, реализованному в классах MarsSea и EarthSea. Такие объекты, как Лес и Равнины, реализованы одинаково. В этом случае вы можете использовать шаблон A Abstract Factory. У нас есть различные иерархии продуктов (море, лес, равнины) с крепкими семейными отношениями, включая наследование (земля, Марс). На изображение снизу показана диаграмма классов, иллюстрирующая, как шаблоны и фабрика Abstract Factory могут использоваться для работы с этими продуктами. Как видите, мы используем наследование для группировки семейств типов местности для продуктов, которые будут производиться на заводе. Это уместно, но требует большой иерархии наследования и недостаточно гибкости.

Реализация

Используя модели A Abstract Factory и Factory Method, мы должны решить в конкретное время, с каким конкретным производителем нам следует работать. Вероятно, это можно сделать, проанализировав значения некоторых флагов. И поскольку мы должны сделать это так или иначе, почему бы не создать фабричный класс для хранения определенных продуктов и их распространения во время инициализации? Таким образом, мы можем избавиться от разных классов и, как мы скоро увидим, использовать их другие преимущества. Ниже приведен пример простого кода, который использует шаблон Prototype в качестве фабрики.

<?php

//// SEA ////
class Sea
{
}
class EarthSea extends Sea
{
}
class MarsSea extends Sea
{
}

//// PLAINS////
class Plains
{
}
class EarthPlains extends Plains
{
}
class MarsPlains extends Plains
{
}

//// FOREST ////
class Forest
{
}
class EarthForest extends Forest
{
}
class MarsForest extends Forest
{
}

// TERRAIN FACTORY
class TerrainFactory
{
    private $sea;
    private $forest;
    private $plain;

    public function  __construct(Sea $sea, Plains $plains, Forest $forest)
    {
        $this->sea = $sea;
        $this->plain = $plains;
        $this->forest = $forest;
    }
    public  function getSea(): Sea
    {
        return clone $this->sea;
    }
    public  function getPlains(): Plains
    {
        return clone $this->plain;
    }
    public  function getForest(): Forest
    {
        return clone $this->forest;
    }
}

$factory = new TerrainFactory(
    new EarthSea(),
    new EarthPlains(),
    new EarthForest()
);
print_r($factory->getSea());
print_r($factory->getPlains());
print_r($factory->getForest());

Как вы можете видеть, экземпляры объектов различных продуктов загружаются в экземпляр конкретной фабрики, такой как TerrainFactory. Когда метод getSea () вызывается в клиентском коде, он возвращает клон объекта Sea, который кэшируется во время инициализации. В результате нам удалось не только сократить несколько классов, но и добиться некоторой гибкости. Вы хотите, чтобы игра происходила на новой планете с морями и лесами, как на Земле, и с равнинами, как на Марсе? Для этого вам не нужно создавать новый класс производителя, а нужно только изменить набор классов, представленных в TerrainFactory, как показано ниже.

$factory = new TerrainFactory(
    new EarthSea(),
    new EarthPlains(),
    new EarthForest()
);

Таким образом, модель Prototype позволяет вам воспользоваться гибкостью, которую предлагает композиция. Но у нас есть нечто большее. В конце концов, мы сохраняем и клонируем объекты во время выполнения программы, что означает, что мы воспроизводим состояние объектов при создании новых продуктов.

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