Реализация шаблона Unit of Work

Чтобы определить, какие требуются операции с базой данных, нужно следить за различными событиями, которые происходят с объектами. Вероятно, самое лучшее место, где это можно сделать, — сами объекты.

Нужно также вести список объектов, составленный для каждой операции с базой данных (вставка, обновление, удаление). Сейчас я собираюсь осветить только операции вставки и обновления. Где лучше сохранять список объектов? Поскольку у нас уже есть объект ob j ectwatcher, можем продолжить разработку.

//Objectwatcher

// …

private $all = array ();

private $dirty = array ();

private $new = array();

private $delete = array(); // В нашем примере не используется

private static $instance;

// …

static function addDirty( woo_domain_DomainObject $obj ) {

$inst = self::instance();

if ( ! in_array( $obj, $inst->new, true ) ) {

$inst->dirty[$inst->globalKey( $obj )] = $obj;

}

}

static function addNew( woo_domain_DomainObject $obj ) {

$inst = self::instance(); // У нас еще нет идентификатора id

$inst->new[] = $obj;

}

static function addClean(woo_domain_DomainObject $obj ) {

$self = self::instance();

unset( $self->dirty[$self->globalKey( $obj )] );
if ( in_array( $obj, $self->new, true ) ) {

$pruned=array();

foreach ( $self->new as $newobj ) {

if ( ! ( $newobj === $obj) ) {

$pruned[]=$newobj;

}

}

$self->new = $pruned;

}

}

function performOperations() {

foreach ( $this->dirty as $key=>$obj ) {

$obj->finder()->update( $obj );

}

foreach ( $this->new as $key=>$obj ) {

$obj->finder()->insert( $obj );

}

$this->dirty = array();

$this->new = array ();

}

Класс ObjectWatcher остается как Identity Map и продолжает выполнять свою функцию отслеживания всех объектов в системе с помощью свойства $all. Этот пример просто добавляет дополнительные функциональные возможности классу. Аспекты шаблона Unit of Work для класса ObjectWatcher показаны на рисунке

Scr05 Реализация шаблона Unit of Work

Объекты описываются как «измененные», если они были изменены после извлечения из базы данных. Измененный объект сохраняется в свойстве массива $dirty (с помощью метода addDirty ()), пока не приходит время обновить базу данных. Клиентский код может решить, что измененный объект не должен подвергаться обновлению, по собственным причинам. Клиентский код может обеспечить это. пометив измененный объект как неизмененный (с помощью метода addClean()). Как и можно было ожидать, вновь созданный объект должен быть добавлен к массиву $new (с помощью метода addNew ()). Для объектов в этом массиве составлено расписание вставки в ба:зу данных. В этих примерах мы не реализуем функцию удаления, но принцип должен быть понятен.

Каждый из методов addDirty() и addNew() добавляет объект к соответствующим свойствам массива. Но метод addClean () удаляет заданный объект из массива $dirty. помечая его как больше не ожидающий обновления.

Когда, наконец, приходит время обрабатывать все объекты, сохраненные в этих массивах, должен быть вызван метод performOprations () (вероятно, из класса контроллера или его вспомогательного класса). Этот метод проходит в цикле по массивам $dirty и $new, обновляя или добавляя объекты.
Класс ObjectWatcher теперь обеспечивает механизм обновления и вставки объектов. Но коду все еще не хватает способа добавления объектов к объекту ObjectWatcher.

Поскольку операции проводятся именно на этих объектах, вероятно, они сами — лучшее место для выполнения такого уведомления. Вот несколько вспомогательных методов, которые можно добавить к классу DomainObject. Также обратите внимание на метод конструктора.

// DomainObject

abstract class woo_domain_DomainObject {

private $id = -1;

function construct( $id=null ) {

if ( is_null( $id ) ) {

$this->markNew();

} else {

$this->id = $id;

}

}

function markNew() {

woo_domain_ObjectWatcher::addNew( $this );

}

function markDeleted() {

woo_domain_ObjectWatcher::addDelete( $this );

}

function markDirty() {

woo_domain_ObjectWatcher::addDirty( $this );

}

function markClean() {

woo domain Obj ectWatcher:: addClean ( $this ),-

}

function setld( $id ) { $this->ld = $id;

}

function getld( ) { return $this->id;

}

function finder() {

return self::getFinder( get_class( $this ) );

}

static function getFinder( $type ) {

return woo domain HelperFactory::getFinder( $type );

}

//…

Прежде чем смотреть на код Unit of Work, стоит отметить, что у класса Domain есть методы finder () и getFinder (). Они работают точно так же, как collection () и getCollection (), запрашивая простой фабричный класс, HelperFactory, чтобы получить объекты Mapper, когда они понадобятся.

Как видите, метод конструктора помечает текущий объект как новый (вызывая markNew ()). если ему не было передано свойство $id. Это похоже на магию, и тут нужно проявлять некоторую осторожность. В таком виде этот код назначает новый объект для вставки в базу данных без какого-либо вмешательства со стороны создателя объектов. Представьте, что новый программист в вашей команде пишет сценарий для временного использования, чтобы протестировать работу программы, решающей поставленную задачу. Здесь нет никаких признаков персистентного кода, поэтому все должно быть достаточно безопасно, не так ли? А теперь представьте эти тестовые объекты, вероятно, с интересными временными именами, которые попадают в постоянное хранилище! Магия — это хорошо, но ясность — гораздо лучше. Наверное, лучше потребовать от клиентского кода передавать какой-то признак конструктору, чтобы отметить новый объект для вставки.

Нужно также добавить следующий фрагмент кода в класс Mapper.

// Mapper

function createObject( $array ) {

$old = $this->getFromMap( $array['id']);

if ( $old ) { return $old; }

$obj = $this->doCreateObject( $array );

$this->addToMap( $obj );

$obj->markClean();

return $obj;

}

Поскольку создание объекта включает его пометку в качестве нового с помощью вызова конструктором метода Objectwatcher::addNew ( ), мы должны вызвать метод markClean (), иначе все без исключения объекты, извлеченные из базы данных, будут сохранены в конце запроса, а это не то, что нам нужно.

Единственное, что осталось сделать. — это добавить вызовы markDirty ( ) к методам в классах Domain Model. Помните, измененный объект — это тот, который изменился после того, как был извлечен из базы данных. Это тот аспект шаблона, который подозрительно напоминает Domain Model. Очевидно, важно гарантировать, чтобы все методы, которые меняют состояние объекта, помечались как модифицирующие. Но поскольку это нужно делать вручную, вероятность ошибки (поскольку человеку свойственно ошибаться) вполне реальна.

Вот некоторые методы объекта Space, вызывающие markDirty ( ).

function setName( $name_s ) {

$this->name = $name_s;

$this->markDirty();

}

function setVenue( woo_domain_Venue $venue ) {

$this->venue = $venue;

$this->markDirty () ,-

}

Вот код для добавления новых объектов venue и Space к базе данных, взятый из класса Command.

$venue = new woo_domain_Venue( null, «The Green Trees» );

$venue->addSpace(

new woo_domain_Space( null, ‘The Space Upstairs’ ) );

$venue->addSpace(

new woo_domain_Space( null, ‘The Bar Stage’ ) );

// Это должно вызываться из контроллера или вспомогательного класса woo_domain_ObjectWatcher: : instance()->performOperations();

Я добавил код отладки к Objectwatcher, поэтому можно увидеть, что происходит по окончании запроса.

inserting The Green Trees inserting The Space Upstairs inserting The Bar Stage

Поскольку объект-контроллер высокого уровня обычно вызывает метод perfогmOperations (), все, что нужно сделать в большинстве случаев, — это создать или модифицировать объект, и класс Unit of Work (OjectWatcher) сделает свою работу только один раз в конце запроса.

Советую прочитать также

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*

Перед отправкой формы: