НЕ МОЛЧИ!!!    Сделай что-нибудь, чтобы остановить войну России в Украине.
...бойтесь людей равнодушных - именно с их молчаливого согласия происходят все самые ужасные преступления на свете.   ("Репортаж с петлёй на шее")

Шаблон проектирования Flyweight(Легковес)

Рассматривается структурный шаблон проектирования Flyweight, приводится описание и пример реализации в коде.

Содержание

  • Применение
  • Реализация
  • Итоги

Применение

Шаблон проектирования Flyweight, он же Легковес, он же Приспособленец, используется в приложениях для экономии оперативной памяти при использовании большого количества однотипных элементов. Под однотипными в данном случае подразумеваются объекты с набором одинаковых свойств, которые различаются своими значениями. Причем лишь часть этих свойств зависит от контекста использования объекта.

Пример использования шаблона

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

А теперь обратите внимание на два существенных момента. Первое, приложению необходимо сформировать большое число однотипных объектов. Второе, создавались объекты, часть свойств которых неизменны, назовем их внутренними. И есть свойства, зависящие от условий использования, например, где находился объект в конкретный момент времени, назовем их внешними. Внутренние свойства можно определить при инициировании объекта, а внешние определяет клиент, запросивший наш объект. Из этого следует, что в нашем конкретном случае при создании объекта транспортного средства мы можем определить лишь модель авто и номерной знак. Это позволит уменьшить объем объекта, а значит существенно сократить размер используемой оперативной памяти массивом таких объектов. Мы можем пойти дальше и формирование значения номерного знака возложить на клиента, еще сократив не только объем, но и количество необходимых объектов, ведь модели авто могут совпадать, а зачем плодить одинаковые сущности?

Надеюсь, суть шаблона Flyweight ясна. Остается добавить, поскольку приложению придется оперировать множеством объектов, то в лучших традициях ООП для их создания обычно используется шаблон проектирования Factory.

Итак, шаблон Flyweight представлен, задача по его использованию определена, приступим к реализации.

Реализация

Создадим класс фабрики, возвращающий наши легковесы — объекты, описывающие автомобильные средства компании.

class CarFactory
{
    private $cars = [];

    public function __construct()
    {       
        $this->cars['car1'] = new Car1(); 
        $this->cars['car2'] = new Car2();
        $this->cars['car3'] = new Car3();
    }

    public function getCar($key)
    {
        if (!array_key_exists($key, $this->cars)) 
        {
            switch ($key) {
                case 'car4' : 
                        $this->cars[$key] = new Car4();
                        break;
                case 'car5' : 
                        $this->cars[$key] = new Car5();
                        break;
                case 'car6' : 
                        $this->cars[$key] = new Car6();
                        break;
                       ...
                default :
                        $res = 'Car with ID = ' .$key . 'is absent';  
            }
            $res = $this->cars[$key];
        } else {
            $res = $this->cars[$key];
        }
        return cars[$key];
    }
}

Теперь создаем систему классов наших транспортных средств. При создании объектов им сразу можно определить ID, модель автомобиля и его номерной знак.

abstract class Car
{
    private $id;
    private $model;
    private $number;

    public abstract function getState($longitude, $latitude, $time);
}


class Car1 extends Car
{
    private $id = 'car1';
    private $model = 'model1';
    private $number = ABC-0001;


    public function getStatus($longitude, $latitude, $time)
    {
        $status = [];
        $status['id'] = $this->id;
        $status['model'] = $this->model;
        $status['number'] = $this->number; 
        $status['longitude'] = $longitude;
        $status['latitude'] = $latitude;
        $status['time'] = $time;
        return $status;
    }
}


    ...

class CarN extends Car
{

    private $id = 'carN';
    private model = 'modelN';
    private number = ABC-10000;

    public function getStatus($longitude, $latitude, $time)
    {
        $status = [];      
        $status['model'] = $this->model;
        $status['number'] = $this->number; 
        $status['longitude'] = $logitude;
        $status['latitude'] = $latitude;
        $status['time'] = $time;
        $return $status;
    }
}

Если предположить, что клиентом выступает браузер пользователя, то обработчик HTTP запроса, возвращающий список свойств транспорта в формате HTML списка ul, мог бы выглядеть примерно так.

require 'CarFactory.php';

$carId = htmlspecialchars($_GET["id"]);
$factory = new CarFactory();
$obj = $factory->getCar($carId);

if (isset($obj)) {
    $today = date('d-m-Y H:i');
    $arr = $obj->getState('30.433526', '50.462099', $today);
    $str = getHtmlString($carId, $arr);
} else {
    $str = 'Car not found';
}
echo $str;


function getHtmlString($carId, $arr)
{
    $res = "<ul id=\"state_list\"><b>Состояние авто \"$carId\":</b>\n";
    foreach ($arr as $key => $value) {
        $res .= '<li>' .$key .' : ' .$value .'</li>';
    }
    $res .= '</ul>';
    return $res;
}

Итоги

Итак, вам был представлен шаблон проектирования Flyweight. Повторюсь, основной целью его применения является экономия ресурсов вычислительных средств. Шаблон эффективен в случае использования большого количества однотипных объектов, часть свойств которых не зависит от контекста.
На этом пока все. Успехов в разработке!