Посетитель (Visitor) — шаблон проектирования
7 февраля 2020
Шаблон предлагает схему, в которой объект-посетитель получает доступ к целевому объекту для выполнения различных обработок и действий. Это позволяет, не меняя класс целевого объекта, выполнять над ним различные операции, которые логически не имеют к нему прямого отношения.
Код реализации паттерна на языке PHP. Скачать исходники (ZIP, 2 Кб)
Шаблон состоит из двух классов:
Для организации доступа Посетителя к объекту Целевого класса, между ними определяется контракт. Для этого в Целевом классе создаётся метод, вида accept($visitor), в теле которого вызывается метод Посетителя visit($this), где $this – это объект Целевого класса. Таким образом, объект Целевого класса передаётся Посетителю.
Теперь перейдём непосредственно к коду.
Затем создаётся объект $table для построения таблицы мебели и ему открывается доступ к дому
Завершающим шагом выводим таблицу со списком мебели
Полный код скрипта test.php
Результат выполнения скрипта test.php
ПРИМЕР ЗАДАЧИ: для проекта дома вывести в таблицу список мебели и её стоимость.
Код реализации паттерна на языке PHP. Скачать исходники (ZIP, 2 Кб)
Шаблон состоит из двух классов:
- Целевой класс – к объектам данного класса нам нужно получить доступ, чтобы выполнить обработки, необходимые для решения задачи;
- Посетитель – объект данного класса получает доступ к объекту Целевого класса и выполняет необходимые обработки.
Для организации доступа Посетителя к объекту Целевого класса, между ними определяется контракт. Для этого в Целевом классе создаётся метод, вида accept($visitor), в теле которого вызывается метод Посетителя visit($this), где $this – это объект Целевого класса. Таким образом, объект Целевого класса передаётся Посетителю.
Теперь перейдём непосредственно к коду.
furniture.class.php
Файл содержит класс мебели Furniture и является Целевым классом, состоит из следующих свойств и методов:- $name – название мебели;
- $price – цена;
- Конструктор __construct($name, $price) – создаёт объект мебели;
- getName() – возвращает название;
- getPrice() – возвращает цену;
- acceptVisitor($visitor) – принятие Посетителя. Метод открывает Посетителю доступ к объекту с помощью вызова метода посетителя $visitor->visit($this), в аргументах которого передаётся объект мебели.
<?
/**
* Мебель
*/
class Furniture
{
/**
* Название
* @var string
*/
protected $name;
/**
* Цена
* @var string
*/
protected $price;
/**
* Конструктор, создаёт объект мебели
* @param string $name - название
* @param string $name - цена
*/
public function __construct($name, $price)
{
$this->name = $name;
$this->price = $price;
}
/**
* Возвращает название
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Возвращает цену
* @return string
*/
public function getPrice()
{
return $this->price;
}
/**
* Принимает посетителя
* @param Visitor $visitor
*/
public function acceptVisitor($visitor)
{
$visitor->visit($this);
}
}
?>
home.class.php
Содержит класс дома Home и тоже представляет Целевой класс, который состоит из следующих свойств и методов:- $name – название дома;
- $furniture –мебель, которая есть в доме. Представляет собой массив ссылок на объекты мебели;
- Конструктор __construct($name) – создаёт объект дома;
- getName() – возвращает название;
- loadById($id) – загружает данные дома из БД по его идентификатору (ID). В демонстративных целях реального обращения к базе данных не осуществляется, вместо этого используется загрузка из статичного хранилища;
- addFurniture($furniture) – добавление мебели в дом;
- acceptVisitor($visitor) – принятие Посетителя. Метод также открывает Посетителю доступ к дому с помощью вызова метода Посетителя $visitor->visit($this). Обратите внимание, что помимо дома, Посетитель ещё получает доступ к мебели этого дома, см. фрагмент обработки $furniture->acceptVisitor($visitor).
<?
/**
* Дом
*/
class Home
{
/**
* Название дома
* @var string
*/
protected $name;
/**
* Мебель дома
* @var Furniture[]
*/
protected $furniture = array();
/**
* Конструктор, создаёт объект дома
* @param string $name - название дома
*/
public function __construct($name = '')
{
$this->name = $name;
}
/**
* Возвращает название дома
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Загружает дом по ID
* @param int $id - идентификатор дома
* @return bool - вернёт 'true' в случае успешной загрузки и 'false' в случае ошибки
*/
public function loadById($id)
{
//Эмуляция загрузки дома из БД
if ($id == 121) {
$this->name = 'Дом из клееного бруса';
//Подключаем класс для работы с мебелью
require_once(__DIR__.'/furniture.class.php');
//Загружаем мебель и добавляем её в дом
$arFurnitureList = array(
array(
'name' => 'Кухонный стол',
'price' => '8000',
),
array(
'name' => 'Стулья на кухню (4 шт)',
'price' => '10000',
),
array(
'name' => 'Кухонные подвесные ящики (3 шт)',
'price' => '19500',
),
array(
'name' => 'Стол в гостинной',
'price' => '12050',
),
array(
'name' => 'Шкаф',
'price' => '11500',
),
array(
'name' => 'Диван',
'price' => '18200',
),
array(
'name' => 'ТВ-тумба',
'price' => '9600',
),
);
foreach ($arFurnitureList as $item) {
$furniture = new Furniture($item['name'], $item['price']);
$this->addFurniture($furniture);
}
return true;
} else {
return false;
}
}
/**
* Добавляет мебель
* @param Furniture $furniture - мебель
* @return bool - вернёт 'true' в случае успешного добавления и 'false' в случае ошибки
*/
public function addFurniture($furniture)
{
$this->furniture[] = $furniture;
}
/**
* Принимает посетителя
* @param Visitor $visitor
*/
public function acceptVisitor($visitor)
{
$visitor->visit($this);
foreach ($this->furniture as $furniture) {
$furniture->acceptVisitor($visitor);
}
}
}
?>
table_furniture_visitor.class.php
Файл содержит следующие элементы:- Visitor – интерфейс Посетителя, в котором объявлен единственный метод visit($obj), служащий для посещения Целевого объекта;
- TableFurnitureVisitor – класс таблицы со списком мебели. Реализует интерфейс Посетителя Visitor, состоит из следующих свойств и методов:
- $home – ссылка на объект Дома (класс Home);
- $furniture – массив ссылок на объекты Мебели (класс Furniture), которые находятся в доме;
- visit($obj) – выполняет посещение объекта (дома или мебели) и сохраняется ссылку на объект в своих свойствах;
- print() – используя данные, ранее полученные во время посещений объектов, строит таблицу со списком мебели и её ценой.
<?
/**
* Интерфейс посетителя
*/
interface Visitor
{
/**
* Выполняет посещение объекта
* @param object $obj
*/
public function visit($obj);
}
/**
* Таблица со сведениями о мебели дома
*/
class TableFurnitureVisitor implements Visitor
{
private $home = null;
private $furniture = array();
/**
* Посещение объекта (дома или мебели). Выполняет добавление сведений в таблицу
* @param Object $obj - посещаемый объект
*/
public function visit($obj)
{
if (get_class($obj) == 'Home') {
$this->home = $obj;
} else {
$this->furniture[] = $obj;
}
}
/**
* Выводит на экран таблицу со сведениями о мебели
* @return string
*/
public function print()
{
//Выводим над таблицей название дома
if ($this->home) {
echo '<div style="margin: 0 0 10px 0;"><b>'.htmlspecialchars($this->home->getName()).'. Состав мебели:</b></div>';
}
//Вывод списка мебели
echo '
<table style="width: 100%; max-width: 600px;">
<tr class="tr_head">
<td style="width: 50%;">Название мебели</td>
<td>Цена</td>
</tr>
';
foreach ($this->furniture as $furniture) {
echo '
<tr>
<td>'.htmlspecialchars($furniture->getName()).'</td>
<td>'.number_format($furniture->getPrice(), 0, ',', ' ').' руб.</td>
</tr>
';
}
echo '</table>';
}
}
?>
test.php
Содержит тестовый скрипт, в котором создаётся объект дома $home и загружаются для него данные из записи c ID = 121//Загружаем объект дома
$home = new Home();
if ($home->loadById(121) == false) {
echo 'Ошибка. Не удалось загрузить дом';
} else {
Затем создаётся объект $table для построения таблицы мебели и ему открывается доступ к дому
//Подключаем класс для работы с таблицей мебели дома
require_once(__DIR__.'/table_furniture_visitor.class.php');
$table = new TableFurnitureVisitor();
//Посещаем дом
$home->acceptVisitor($table);
Завершающим шагом выводим таблицу со списком мебели
//Выводим таблицу на экран
$table->print();
Полный код скрипта test.php
<html>
<head>
<title>Тестовый скрипт</title>
<style>
body {
padding: 15px 15px;
font-family: arial, sans-serif;
font-size: 10.5pt;
}
table {
border-collapse: collapse;
}
table tr.tr_head {
background-color: #eee;
}
table td {
padding: 5px 8px;
border: 1px solid #333;
}
</style>
</head>
<body>
<?
//Подключаем класс дома
require_once(__DIR__.'/home.class.php');
//Загружаем объект дома
$home = new Home();
if ($home->loadById(121) == false) {
echo 'Ошибка. Не удалось загрузить дом';
} else {
//Подключаем класс для работы с таблицей мебели дома
require_once(__DIR__.'/table_furniture_visitor.class.php');
$table = new TableFurnitureVisitor();
//Посещаем дом
$home->acceptVisitor($table);
//Выводим таблицу на экран
$table->print();
}
?>
</body>
</html>
Результат выполнения скрипта test.php
| Название мебели | Цена |
| Кухонный стол | 8 000 руб. |
| Стулья на кухню (4 шт) | 10 000 руб. |
| Кухонные подвесные ящики (3 шт) | 19 500 руб. |
| Стол в гостинной | 12 050 руб. |
| Шкаф | 11 500 руб. |
| Диван | 18 200 руб. |
| ТВ-тумба | 9 600 руб. |