Реализация загрузки по требованию
с помощью виртуальных прокси-объектов и средств PHP
Автор — Геннадий Круглов.
По мере освоения программистами объектно-ориентированной парадигмы для отображения бизнес-логики все чаще используется шаблон проектирования Domain Model (модель предметной области)[1]. Широко известны две разновидности Domain Model: простая и сложная. Простая, по сути, является объектным отображением реляционной схемы и содержит по одному объекту предметной области на каждую таблицу. При ее использовании первичной является реляционная модель. Сложная модель более адекватно отображает сложную бизнес-логику и, как правило, содержит иерархии наследования и множество взаимосвязанных объектов. При использовании сложной модели предметной области и реляционной базы данных для хранения объектов, что на сегодняшний день очень актуально, востребованным становится объектно-реляционное отображение (ORM — object-relational mapping), т. к. на первый план при проектировании приложения выходит объектно-ориентированная модель и поэтому разработчик уже не оперирует терминами таблиц и внешних ключей.
При загрузке объекта предметной области из реляционной базы данных необходимо обеспечить автоматическую загрузку связанных с ним объектов. В противном случае придется прописывать загрузку всех объектов вручную, что приведет к «размазыванию» бизнес-логики из-за дополнительных зависимостей от базы данных.
Автоматическая загрузка всей структуры взаимосвязанных объектов позволяет существенно упростить программирование, но в то же время может привести к нежелательным последствиям: нерациональному использованию ресурсов, например загрузка глубокой иерархии объектов, когда требуется лишь только ее часть; зацикливанию программы при наличии двунаправленной ассоциации между объектами.
Нежелательных последствий можно избежать, если использовать шаблон проектирования Lazy Load (загрузка по требованию)[1]. Загрузка по требованию позволяет прерывать процесс загрузки и загружать нужные объекты только тогда, когда это действительно необходимо.
Существует несколько способов реализации Lazy Load. Один из таких способов описан в шаблоне проектирования Virtual Proxy (виртуальный прокси-объект)[2][3].
Virtual Proxy имитирует объект предметной области, однако в действительности содержит только данные необходимые для загрузки реального объекта. При этом загрузка реального объекта произойдет только тогда, когда будет вызван один из методов виртуального прокси-объекта.
В статически типизированных языках приходится создавать отдельный виртуальный прокси-объект на каждый класс предметной области, что приводит к усложнению программы и дополнительным временным затратам. Динамически типизированные языки позволяют обойти этот недостаток.
PHP является динамически типизированным и позволяет более эффективно
реализовать шаблон Virtual Proxy. Кроме того, PHP поддерживает механизм
перехвата обращений к свойствам и методам. В основе этого
механизма лежат полномочные методы[4] __get(), __set()
и __call(), вызываемые автоматически, если соответствующие
метод или поле не определены в классе. Полномочный метод
__call() позволяет существенно упростить создание единственного
класса Virtual Proxy для всех классов предметной области. Экземпляры такого
класса могут выступать в роли объектов-посредников для любых объектов
предметной области.
class VirtualProxy {
private $real_class_name = ''; // класс реального объекта
private $id = ''; // идентификатор объекта предметной области
private $real_object = null; // ссылка на реальный объект
function __construct($real_class_name = '', $id = '') {
$this->real_class_name = $real_class_name;
$this->id = $id;
}
public function getRealClassName() {
return $this->real_class_name;
}
public function setRealClassName($value = '') {
$this->real_class_name = $value;
}
public function getId() {
return $this->id;
}
public function setId($value = '') {
$this->id = $value;
}
function __call($method_name, $args) {
if (empty($this->real_object)) {
// если объект предметной области не загружен, то происходит его загрузка из БД
// в данном примере используется код ORM CMS “Blackbox”
$this->real_object = uow::getInstance()->execute($this->real_class_name,
'findById', array('id' => $this->id));
}
if (!empty($this->real_object)) {
$method = new ReflectionMethod(get_class($this->real_object), $method_name);
if ($method->isPublic() && !$method->isAbstract()) {
/* если реальный объект загружен, и затребованный метод может быть вызван,
Virtual Proxy делегирует вызов метода реальному объекту */
return $method->invoke($this->real_object, $args);
}
}
}
}
В листинге метод __call() определяет, загружен ли объект
предметной области и если не загружен, то загружает его
из базы данных и сохраняет в поле $real_object.
Затем __call() с помощью механизма рефлексии определяет,
является ли затребованный метод открытым и конкретным,
и в случае успеха делегирует объекту предметной области вызов
затребованного метода.
Теперь для создания виртуального прокси-объекта любого класса предметной области достаточно лишь вызвать конструктор и инициализировать его значениями идентификатора и имени класса реального объекта.
Литература
- ↑ 1.0 1.1 Мартин Фаулер Архитектура корпоративных программных приложений. — М.: «Вильямс», 2007.
- ↑ Крэг Ларман Применение UML и шаблонов проектирования. — М.: «Вильямс», 2002.
- ↑ Марк Гранд Шаблоны проектирования в Java. — СПб.: «Символ-Плюс», 2004.
- ↑ Энди Гутманс, Стиг Баккен, Дерик Ретанс PHP 5. Профессиональное программирование. — СПб.: «Символ-Плюс», 2006.
