Стратегия (Strategy) — шаблон проектирования
5 февраля 2020
Шаблон предлагает схему для динамического изменения поведения (функционала) метода объекта. Это позволяет разбить большие обработки на несколько мелких, снизить сложность и сделать архитектуру проекта гибче.
Код реализации паттерна на языке PHP. Скачать исходники (ZIP, 3 Кб)
Шаблон состоит из трёх элементов:
Теперь перейдём к коду.
Обратите внимание, что метод export() делегируют формирование экспортной версии отчёта классам форматов. Именно в этом и заключается смысл шаблона, когда поведение отчёта (Контекста) зависит от установленного формата (Стратегии).
Рассмотрим классы подробнее.
Далее происходит экспорт отчёта. Как мы помним, по умолчанию отчёт выгружается в простом текстовом формате
Потом устанавливаем HTML-формат и выводим содержимое
А в конце ставим формат MediaWiki и так же экспортируем отчёт
Полный код скрипта test.php
Результат выполнения скрипта test.php (вид в консоли или в браузере, в режиме просмотра исходного кода)
ПРИМЕР ЗАДАЧИ: реализовать возможность вывода отчёта в трёх форматах – текстовый, HTML и MediaWiki.
Код реализации паттерна на языке PHP. Скачать исходники (ZIP, 3 Кб)
Шаблон состоит из трёх элементов:
- Контекст – класс, у объекта которого мы собираемся изменить поведение метода. Контекст содержит свойство-стратегию и метод для изменения его значения. В нашей задаче Контекстом является отчёт;
- Стратегия – класс, определяющий поведение метода Контекста. Для каждого поведения создаётся свой класс. Роль Стратегий у нас будут выполнять форматы отчётов: простой текст, HTML и MediaWiki;
- Интерфейс стратегии – интерфейс или абстрактный класс, объявляющие свойства и методы Стратегии, которые будет использовать Контекст. Другими словами, это контракт между Контекстом и Стратегией.
Теперь перейдём к коду.
report.class.php
Файл содержит класс Report, представляющий отчёт. Класс состоит из следующих свойств и методов:- $content – содержимое отчёта. Представляет собой массив, состоящий из параграфов, которые в свою очередь состоят из двух записей - заголовка (ключ title) и описания (ключ description);
- $format – формат отчёта, представленный в виде объекта класса AbstractReportFormat. В терминологии шаблона данное свойство содержит Стратегию;
- Конструктор __construct($format = false) – создаёт объект отчёта. В аргументе указывается формат отчёта, а если аргумент не задан, то по умолчанию устанавливается формат «Простой текст», который представлен объектом класса ReportFormatText (дочерний класс от AbstractReportFormat);
- setFormat($format) – устанавливает формат отчёта (изменяет Стратегию);
- addParagraph($title, $description) – добавляет в содержимое отчёта новый параграф. В аргументах метода указывается заголовок параграфа и текстовое описание;
- clear() – очищает содержимое отчёта;
- export() – возвращает отчёт в установленном формате.
Обратите внимание, что метод export() делегируют формирование экспортной версии отчёта классам форматов. Именно в этом и заключается смысл шаблона, когда поведение отчёта (Контекста) зависит от установленного формата (Стратегии).
<?
//Подключаем классы для работы с форматами отчёта
require_once(__DIR__.'/report_format.classes.php');
/**
* Отчёт
*/
class Report
{
/**
* Содержимое отчёта. Массив, состоящий из элементов-массивов, которые в свою очередь
* состоят из двух записей - заголовка (ключ 'title') и описания (ключ 'description')
* @var array
*/
private $content = array();
/**
* Формат отчёта
* @var AbstractReportFormat
*/
private $format;
/**
* Конструктор. Создаёт объект отчёта
* @param AbstractReportFormat $format - формат отчёта. По умлочанию устанавливается формат "Простой текст"
*/
public function __construct($format = false)
{
//Установка формата отчёта
if ($format == false) {
$format = new ReportFormatText();
}
$this->setFormat($format);
}
/**
* Устанавливает формат отчёта
* @param AbstractReportFormat $format - формат отчёта
*/
public function setFormat($format)
{
$this->format = $format;
}
/**
* Добавляет в отчёт параграф
* @param string $title - заголовок
* @param string $description - описание
*/
public function addParagraph($title, $description)
{
$this->content[] = array(
'title' => $title,
'description' => $description
);
}
/**
* Очищает содержимое отчёта
*/
public function clear()
{
$this->content = array();
}
/**
* Экспорт отчёта
* @return string - возвращает строку с содержимым отчёта
*/
public function export()
{
return $this->format->export($this->content);
}
}
?>
report_format.classes.php
Файл содержит классы с форматами отчётов:- AbstractReportFormat – абстрактный класс формата;
- ReportFormatText – стандартный формат «Простой текс»;
- ReportFormatHTML – формат HTML;
- ReportFormatMediaWiki – формат MediaWiki.
Рассмотрим классы подробнее.
AbstractReportFormat
Представляет абстрактный класс формата, состоит из одного абстрактного метода экспорта export(). Данный класс служит для упрощения объявления и поддержки единообразия конкретных форматов отчёта. Задаёт контракт между Контекстом и Стратегией./**
* Абстрактный класс "Формат отчёта"
*/
abstract class AbstractReportFormat
{
/**
* Экспорт отчёта
* @param array - содержимое отчёта. Массив, состоящий из элементов-массивов, которые в свою очередь
* состоят из двух записей - заголовка (ключ 'title') и описания (ключ 'description')
* @return string - возвращает строку с содержимым отчёта
*/
abstract public function export($reportContent);
}
ReportFormatText
Класс представляет стандартный текстовый формат./**
* Формат отчёта "Простой текст"
*/
class ReportFormatText extends AbstractReportFormat
{
/**
* Экспорт отчёта
* @param array - содержимое отчёта. Массив, состоящий из элементов-массивов, которые в свою очередь
* состоят из двух записей - заголовка (ключ 'title') и описания (ключ 'description')
* @return string - возвращает строку с содержимым отчёта
*/
function export($reportContent)
{
$return = array();
foreach ($reportContent as $v) {
$paragraph = array();
//Заголовок
if (isset($v['title']) && $v['title'] != '') {
$paragraph[] = mb_strtoupper($v['title'], 'utf-8');
}
//Описание
if (isset($v['description']) && $v['description'] != '') {
$paragraph[] = $v['description']."\n";
}
if (count($paragraph) > 0) {
$return[] = implode("\n", $paragraph);
}
}
return implode("\n", $return);
}
}
ReportFormatHTML
Класс представляет отчёт в формате HTML./**
* Формат отчёта "HTML"
*/
class ReportFormatHTML extends AbstractReportFormat
{
/**
* Экспорт отчёта
* @param array - содержимое отчёта. Массив, состоящий из элементов-массивов, которые в свою очередь
* состоят из двух записей - заголовка (ключ 'title') и описания (ключ 'description')
* @return string - возвращает строку с содержимым отчёта
*/
function export($reportContent)
{
$return = array();
$titleNum = 0;
foreach ($reportContent as $v) {
$paragraph = array();
//Заголовок
if (isset($v['title']) && $v['title'] != '') {
$titleNum++;
if ($titleNum == 1) {
$paragraph[] = '<h1>'.$v['title'].'</h1>'."\n";
} else {
$paragraph[] = '<h2>'.$v['title'].'</h2>'."\n";
}
}
//Описание
if (isset($v['description']) && $v['description'] != '') {
$paragraph[] = str_replace("\n", "\n<br>", $v['description']).'<br><br>'."\n";
}
if (count($paragraph) > 0) {
$return[] = implode('', $paragraph)."\n";
}
}
return implode('', $return);
}
}
ReportFormatMediaWiki
Класс представляет отчёт в формате MediaWiki./**
* Формат отчёта "MediaWiki"
*/
class ReportFormatMediaWiki extends AbstractReportFormat
{
/**
* Экспорт отчёта
* @param array - содержимое отчёта. Массив, состоящий из элементов-массивов, которые в свою очередь
* состоят из двух записей - заголовка (ключ 'title') и описания (ключ 'description')
* @return string - возвращает строку с содержимым отчёта
*/
function export($reportContent)
{
$return = array();
$titleNum = 0;
foreach ($reportContent as $v) {
$paragraph = array();
//Заголовок
if (isset($v['title']) && $v['title'] != '') {
$titleNum++;
if ($titleNum == 1) {
$paragraph[] = '== '.$v['title'].' =='."\n";
} else {
$paragraph[] = '=== '.$v['title'].' ==='."\n";
}
}
//Описание
if (isset($v['description']) && $v['description'] != '') {
$paragraph[] = $v['description']."\n";
}
if (count($paragraph) > 0) {
$return[] = implode('', $paragraph)."\n";
}
}
return implode('', $return);
}
}
test.php
Содержит тестовый скрипт, в котором создаётся отчёт $report (объект класса Report) и затем в него добавляются параграфы с заголовком и описанием//Создаём отчёт и добавляем в него текст-пантограммы
$report = new Report();
$report->addParagraph('Заголовок первый', 'Съешь ещё этих мягких французских булок, да выпей чаю.');
$report->addParagraph('Заголовок второй', 'В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!');
$report->addParagraph('Заголовок третий', 'Широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства.');
Далее происходит экспорт отчёта. Как мы помним, по умолчанию отчёт выгружается в простом текстовом формате
//Выводим отчёт (по умолчанию отчёт будет выведен в простом текстовом формате)
echo "Формат «Текст»\n";
echo "--------------------\n";
echo $report->export();
Потом устанавливаем HTML-формат и выводим содержимое
//Устанавливаем формат отчёта HTML и выводим его
echo "\nФормат «HTML»\n";
echo "--------------------\n";
$report->setFormat(new ReportFormatHTML());
echo $report->export();
А в конце ставим формат MediaWiki и так же экспортируем отчёт
//Устанавливаем формат отчёта MediaWiki и выводим его
echo "\nФормат «MediaWiki»\n";
echo "--------------------\n";
$report->setFormat(new ReportFormatMediaWiki());
echo $report->export();
Полный код скрипта test.php
<?
//Подключаем класс для работы с отчётом
require_once(__DIR__.'/report.class.php');
//Создаём отчёт и добавляем в него текст-пантограммы
$report = new Report();
$report->addParagraph('Заголовок первый', 'Съешь ещё этих мягких французских булок, да выпей чаю.');
$report->addParagraph('Заголовок второй', 'В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!');
$report->addParagraph('Заголовок третий', 'Широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства.');
//Выводим отчёт (по умолчанию отчёт будет выведен в простом текстовом формате)
echo "Формат «Текст»\n";
echo "--------------------\n";
echo $report->export();
//Устанавливаем формат отчёта HTML и выводим его
echo "\nФормат «HTML»\n";
echo "--------------------\n";
$report->setFormat(new ReportFormatHTML());
echo $report->export();
//Устанавливаем формат отчёта MediaWiki и выводим его
echo "\nФормат «MediaWiki»\n";
echo "--------------------\n";
$report->setFormat(new ReportFormatMediaWiki());
echo $report->export();
?>
Результат выполнения скрипта test.php (вид в консоли или в браузере, в режиме просмотра исходного кода)
Формат «Текст»
--------------------
ЗАГОЛОВОК ПЕРВЫЙ
Съешь ещё этих мягких французских булок, да выпей чаю.
ЗАГОЛОВОК ВТОРОЙ
В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!
ЗАГОЛОВОК ТРЕТИЙ
Широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства.
Формат «HTML»
--------------------
<h1>Заголовок первый</h1>
Съешь ещё этих мягких французских булок, да выпей чаю.<br><br>
<h2>Заголовок второй</h2>
В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!<br><br>
<h2>Заголовок третий</h2>
Широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства.<br><br>
Формат «MediaWiki»
--------------------
== Заголовок первый ==
Съешь ещё этих мягких французских булок, да выпей чаю.
=== Заголовок второй ===
В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!
=== Заголовок третий ===
Широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства.