Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
aaaa_moi_glazaa.docx
Скачиваний:
1
Добавлен:
01.05.2025
Размер:
259 Кб
Скачать
☆

Table Data Gateway (Шлюз к данным таблицы)

Паттерн проектирования Table Data Gateway

Описание Table Data Gateway

Объект выступает в качестве шлюза между данными в приложении и в БД. Один объект работает сразу со всеми записями в таблице.

Сочетание SQL-запросов и логики приложения может вызвать достаточно много проблем. Есть разработчики, которые не сильны в SQL, а есть и те, кто в этом преуспел. Администраторы SQL-серверов должны иметь возможность быстро найти SQL-код, чтобы понимать каким образом настраивать и развивать сервера.

Объект шлюза к таблице содержит все запросы SQL для доступа к отдельной таблице или представлению (view): выборка, обновление, вставка, удаление (CRUD). Остальной код, для взаимодействия с БД, обращается к методам объекта шлюза.

Пример: объект шлюза PersonGateway содержит методы для доступа к таблице person в БД. Методы содержат SQL-код для выборки, вставки, обновления и удаления. Объект может содержать специальную выборку, например поиск по компании.

ВАРИАНТ 2:

Аннотация:

Шлюз таблицы данных- объект, выполняющий роль шлюза (Gateway) к базе данных.

Эскиз:

Проблемы:

Использование SQL в логике приложений может быть связано с некоторыми проблемами. Не все разработчики владеют языком SQL или хорошо в нем разбираются. В свою очередь, администраторы СУБД должны иметь удобный доступ к командам SQL для настройки и расширения своих баз данных.

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

Принцип действия:

Интерфейс шлюза таблицы данных чрезвычайно прост. Обычно он включает в себя несколько методов поиска, предназначенных для извлечения данных, а так же методы обновления, вставки и удаления. Каждый метод передаёт входные параметры вызову соответствующей команды SQL и выполняет её в контексте установленного соединения с базой данных. Как правило, это типовое решение не имеет состояний, поскольку всего лишь передаёт данные в таблицу и из таблицы.

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

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

Шлюз таблицы данных хорошо сочетается с модулем таблицы (Table Module). Если все обновления таблиц выполняются через шлюз таблицы данных, результирующие данные могут быть основаны на виртуальных, а не на реальных таблицах, что уменьшает зависимость кода от базы данных.

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

Как правило, для каждой таблицы базы данных создаётся собственный шлюз таблицы данных. Впрочем, в наиболее простых случаях можно ограничиться разработкой одного шлюза таблицы данных, который будет включать в себя все методы для всех таблиц. Кроме того, отдельные шлюзы таблицы данных могут быть созданы для представлений (виртуальных таблиц) и даже для некоторых запросов, не хранящихся в базе данных в форме представлений. Конечно же, шлюз таблицы данных для представления не сможет обновлять данные и поэтому не будет обладать соответствующими методами. Тем не менее, если возможно самим обновлять таблицы, инкапсуляция процедур обновления в методах типового решения шлюз таблицы данных– прекрасный выбор.

Назначение:

Принимая решение об использовании шлюза таблицы данных, как, впрочем, и шлюза записи данных (Row Data Gateway), необходимо подумать о том, следует ли вообще обращаться к шлюзу и если да, то к какому именно.

Шлюз таблицы данных– это наиболее простое типовое решение интерфейса базы данных, поскольку оно замечательно отображает таблицы или записи баз данных на объекты. Кроме того, шлюз таблицы данных естественным образом инкапсулирует точную логику доступа к источнику данных. Крайне редко используют это типовое решение с моделью предметной области, потому что гораздо большей изолированности модели предметной областиот источника данных можно добиться с помощью преобразователя данных (Data Mapper).

Типовое решение шлюз таблицы данных особенно хорошо сочетается с модулем таблицы. Методы шлюза таблицы данных возвращают структуру данных в виде множеств записей, с которыми затем работает модуль таблицы . На самом деле другой подход отображения базы данных для модуля таблицы придумать просто невозможно.

Подобно шлюзу записи данных, шлюз таблицы данных прекрасно подходит для использования в сценариях транзакции (Transaction Script). В действительности выбор одного из нескольких типовых решений зависит только от того, как они обрабатывают множественные результаты. Некоторые предпочитают осуществлять передачу данных посредством объекта переноса данных, однако мне это решение кажется более трудоемким (если только этот объект уже не был реализован где-нибудь в другом месте вашего проекта). Рекомендуется использовать шлюз таблицы данныхв том случае, если его представление результирующего множества данных подходит для работы сосценарием транзакции.

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

Одно из преимуществ использования шлюза таблицы данныхдля инкапсуляции доступа к базе данных состоит в том, что этот интерфейс может применяться и для обращения к базе данных с помощью средств языка SQL, и для работы с хранимыми процедурами. Более того, хранимые процедуры зачастую сами организованы в виде шлюзов таблицы данных. В этом случае хранимые процедуры, предназначенные для вставки и обновления данных, инкапсулируют реальную структуру таблицы. В свою очередь, процедуры поиска могут возвращать представления, что позволяет скрыть фактическую структуру используемой таблицы.

ШЛЮЗ ЗАПИСИ ДАННЫХ

Аннотация:

Шлюз записи данных - объект, выполняющий роль шлюза (Gateway) к отдельной записи источника данных. Каждой строке таблицы базы данных соответствует свой экземпляр шлюза записи данных.

Эскиз:

Проблемы:

Реализация доступа к базам данных в объектах, расположенных в оперативной памяти, имеет ряд недостатков. Прежде всего, если этим объектам присуща собственная бизнес-логика, добавление кода доступа к базе данных значительно повышает их сложность. Кроме того, это серьезно усложняет тестирование. Если объекты, расположенные в оперативной памяти, связаны с базой данных, тестирование выполняется крайне медленно из-за проблем, вызванных необходимостью доступа к базе данных. Особенно раздражает, когда приходится осуществлять доступ к нескольким базам данных, имеющим небольшие, но крайне досадные расхождения к реализации SQL.

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

Принцип действия:

Шлюз записи данных выступает в роли объекта, полностью повторяющего одну запись, например одну строку таблицы базы данных. Каждому столбцу таблицы соответствует поле записи. Обычно шлюз записи данныхдолжен выполнять все возможные преобразования типов источника данных в типы, используемые приложением, однако эти преобразования весьма просты. Рассматриваемое типовое решение содержит все данные о строке, поэтому клиент имеет возможность непосредственного доступа к шлюзу записи данных. Шлюз выступает в роли интерфейса к строке данных и прекрасно подходит для применения в сценариях транзакции (Transaction Script).

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

Иногда шлюз записи данных трудно отличить от активной записи(Active Record). В этом случае следует обратить внимание на наличие какой-либо логики домена; если она есть, значит, это активная запись. Реализация шлюза записи данных должна включать в себя только логику доступа к базе данных и никакой логики домена.

Как и другие формы табличной инкапсуляции, шлюз записи данных можно применять не только к таблице, но и к представлению или запросу. Конечно же, в последних случаях существенно осложняется выполнение обновлений, поскольку приходиться обновлять таблицы, на основе которых были созданы соответствующие представления или запросы. Кроме того, если с одними и теми же таблицами работают два шлюза записи данных, обновление второго шлюза может аннулировать обновление первого. Универсального способа предотвратить эту проблему не существует; разработчикам просто необходимо следить за тем, как построены виртуальные шлюзы записи данных. В конце концов, это же может случиться и с обновляемыми представлениями. Разумеется, можно вообще не реализовать методы обновления.

Назначение:

Принимая решение об использовании шлюза записи данных, необходимо подумать о двух вещах: следует ли вообще использовать шлюз, и если да, то какой именно – шлюз записи данных или шлюз таблицы данных (Table Data Gateway).

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

Не используют шлюз записи данных с моделью предметной области (Domain Model). Если отображение на объекты домена достаточно простое, его можно реализовать и с помощью активной записи, не добавляя дополнительный слой кода. Если же отображение сложное, для его реализации рекомендуется применить преобразователь данных (Data Mapper). Последний лучше справляется с отделением структуры данных от объектов домена, потому что объектам домена не нужно знать о структуре базы данных. Конечно же, шлюз записи данных можно использовать, чтобы скрыть структуру базы данных от объектов домена. Это очень удобно, если вы собираетесь изменить структуру базы данных и не хотите менять логику домена. Тем не менее в этом случае у вас появится три различных представления данных: одно в бизнес-логике, одно в шлюзе записи данных и еще одно в базе данных. Для крупномасштабных систем это слишком много. Поэтому обычно используют шлюзы записи данных, отражающие структуру базы данных.

Интересно, что шлюз записи данных и преобразователь данных вполне могут сосуществовать. Несмотря на то, что с первого взгляда это кажется излишней тратой сил, сочетание шлюза записи данных и преобразователя данных может оказаться весьма эффективным в том случае, если первый автоматически генерируется на основе метаданных, а второй создается вручную.

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

ПАТТЕРН ПРЕОБРАЗОВАТЕЛЬ ДАННЫХ

Аннотация:

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

Эскиз:

Проблемы:

Объекты и реляционные СУБД используют разные механизмы структурирования данных. В реляционных базах данных не отображаются многие характеристики объектов, в частности коллекции и наследование. При построении объектной модели с большим объемом бизнес-логики эти механизмы позволяют лучше организовать данные и соответствующее им поведение. С другой стороны, использование подобных механизмов приводит к несовпадению объектной и реляционной схем.

Объектная модель и реляционная СУБД должны обмениваться данными. Несовпадение схем делает эту задачу крайне сложной. Если объект "знает" о структуре реляционной базы данных, изменение одного из них приводит к необходимости изменения другого.

Типовое решение преобразователь данных представляет собой слой программного обеспечения, которое определяет объекты, расположенные в оперативной памяти, от базы данных. В функции преобразователя данных входит передача данных между объектами и базой данных и изоляция их друг от друга. Благодаря использованию этого типового решения объекты, расположенные в оперативной памяти, могут даже "не подозревать" о самом факте присутствия базы данных. Им не нужен SQL-интерфейс и тем более схема базы данных. (В свою очередь, схема базы данных никогда "не знает" об объектах, которые ее используют.) Более того, преобразователь данныхполностью скрыт от уровня домена.

Принцип действия:

Основной функцией преобразователя данных является отделение домена от источника данных, однако, реализовать эту функцию можно лишь с учётом множества деталей. Кроме того, конструкция слоёв отображения также может быть реализована по-разному. Поэтому многие приведённые ниже советы носят достаточно общий характер.

Рассмотрим пример элементарного преобразователя данных. Структура этого слоя достаточно проста и, возможно, не стоит тех усилий, которые могут быть потрачены на её реализацию. Кроме того, простая структура преобразователя влечёт за собой применение более простых (и поэтому лучших) типовых решений, что вряд ли подойдёт для реальных систем. Тем не менее, начинать объяснение новых идей лучше именно на простых примерах.

В этом примере есть классы Person и PersonMapper. Для загрузки данных в объект Person клиент вызывает метод поиска класса PersonMapper. Преобразователь использует коллекцию объектов (Identity Map) для проверки, загружены ли данные о запрашиваемом лице; если нет, он их загружает.

Клиент указывает преобразователю на необходимость сохранить объект домена. Преобразователь извлекает данные из объекта домена и отсылает их в базу данных.

При необходимости весь слой преобразователя данных может быть заменён, например, чтобы провести тестирование или чтобы использовать один и тот же уровень домена для работы с несколькими базами данных.

Рассмотренный элементарный преобразователь данных всего лишь отображает таблицу базы данных на эквивалентный ей объект по принципу "столбец-на-поле". Преобразователи должны уметь обрабатывать классы, поля которых объединяются одной таблицей, классы, соответствующие нескольким таблицам, классы с наследованием, а также справляться со связыванием загруженных объектов. Все типовые решения объектно-реляционного отображения, рассмотренные в этой книге, так или иначе направлены на решение подобных проблем. Как правило, эти решения легче создавать на основе преобразователя данных, чем с помощью каких-либо других средств.

Для выполнения вставки и обновления данных слой отображения должен знать, какие объекты были изменены, какие созданы, а какие уничтожены. Кроме того все эти действия нужно каким-то образом "уместить" в рамки транзакции. Хороший способ организовать механизм обновления – использовать типовое решение единица работы (Unit of Work).

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

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

Как правило, объекты тесно связаны между собой, поэтому на каком-то этапе загрузку данных следует прерывать. В противном случае выполнение одного запроса может привести к загрузке всей базы данных. Для решения этой проблемы и одновременной минимизации влияния на объекты, расположенные в памяти, слой отображения использует загрузку по требованию (Lazy Load). По этой причине объекты приложения не могут совсем ничего не "знать" о слое отображения. Скорее всего, они должны быть "осведомлены" о методах поиска и некоторых других механизмах.

В приложении может быть один или несколько преобразователей данных. Если код для преобразователей пишется вручную, рекомендуется создать по одному преобразователю для каждого класса домена или для корневого класса в иерархии доменов. Если приложение достаточно большое, методов поиска может оказаться слишком много для одного преобразователя, поэтому лучше разбить их на несколько классов, создав отдельный класс для каждого класса домена или корневой класс в иерархии доменов. Таким образом, появится масса небольших классов с методами поиска, однако, теперь разработчику будет гораздо проще найти то, что ему нужно.

Как и все другие поисковые механизмы баз данных, упомянутые методы поиска должны использовать коллекцию объектов, чтобы отслеживать, какие записи уже были считаны из базы данных.

Назначение:

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

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

Разумеется, за все удобства надо платить. "Ценой" использования преобразователя данных является необходимость реализации дополнительного слоя кода, чего можно избежать, применив, скажем, активную запись (Active Record). Поэтому основным критерием выбора того или иного типового решения является сложность бизнес-логики. Если бизнес-логика довольно проста, ее, скорее всего, можно реализовать и без применения модели предметной области или преобразователя данных. В свою очередь, реализация более сложной логики невозможна без использования модели предметной области и, как следствие этого, преобразователя данных.

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

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

ПАТТЕРН АКТИВНАЯ ЗАПИСЬ

Аннотация:

Активная запись - объект, выполняющий роль оболочки для строки таблицы или представления базы данных. Он инкапсулирует доступ к базе данных и добавляет к данным логику домена.

Эскиз:

Проблемы:

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

Принцип действия:

В основе типового решения активная запись лежит модель предметной области (Domain Model), классы которой повторяют структуру записей используемой базы данных. Каждая активная запись отвечает за сохранение и загрузку информации в базу данных, а так же за логику домена, применяемую к данным. Это может быть вся бизнес-логика приложения. Впрочем, иногда некоторые фрагменты логики домена содержаться в сценариях транзакций (Transaction Script), а общие элементы кода, ориентированные на работу с данными – в активной записи.

Структура данных активной записи должна в точности соответствовать таковой в таблице базы данных: каждое поле объекта должно соответствовать одному столбцу таблицы. Значения полей следует оставлять такими же, какими они были получены в результате выполнения SQL-команд; никакого преобразования на этом этапе делать не нужно. Активная запись может применяться к таблицам или представлениям (хотя в последнем случае реализовать обновления будет значительно сложнее). Использование представлений особенно удобно при составлении отчётов.

Как правило, типовое решение активная запись включает в себя методы, предназначенные для выполнения следующих операций:

  • создание экземпляра активной записи на основе строки, полученной в результате выполнения SQL-запроса;

  • создание нового экземпляра активной записи для последующей вставки в таблицу;

  • статические методы поиска, выполняющие стандартные SQL-запросы и возвращающие активные записи;

  • обновление базы данных и вставка в неё данных из активной записи;

  • извлечение и установка значений полей (get- и set- методы);

  • реализация некоторых фрагментов бизнес-логики.

Методы извлечения и установки значений полей могут выполнять и другие действия, например преобразование типов SQL в типы, используемые приложением. Кроме того, get-метод может возвращать соответствующуюактивную запись таблицы, с которой связана текущая таблица (путём просмотра первой).

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

Активная запись очень похожа на шлюз записи данных (Row Data Gateway). Принципиальное отличие между ними состоит в том, что шлюз записи данных содержит только логику доступа к базе данных, в то время какактивная запись содержит и логику доступа к данным, и логику домена. Как это часто бывает в мире программного обеспечения, граница между упомянутыми типовыми решениями весьма приблизительна, однако игнорировать её всё-таки не следует.

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

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

Назначение:

Активная запись хорошо подходит для реализации не слишком сложной логики домена, в частности операций создания, считывания, обновления и удаления. Кроме того, она прекрасно справляется с извлечением и проверкой на правильность отдельной записи.

Как уже отмечалось, при разработке модели предметной области основная проблема заключается в выборе между активной записью и преобразователем данных (Data Mapper). Преимуществом активной записиявляется простота ее реализации. Недостаток же состоит в том, что активные записи хороши только тогда, когда точно отображаются на таблицы базы данных (изоморфная схема). Если бизнес-логика приложения достаточно сложна, наверняка захочется использовать имеющиеся отношения, коллекции, наследование и т.п. Все это не слишком хорошо отображается на активную запись, а добавление этих элементов «по частям» приведет к страшной неразберихе. В подобных ситуациях лучше воспользоваться преобразователем данных.

Еще одним недостатком использования активной записи является тесная зависимость структуры ее объектов от структуры базы данных. В этом случае изменить структуру базы данных или активной записи довольно сложно, а ведь по мере развития проекта подобная необходимость возникает очень и очень часто.

Активную запись хорошо сочетать со сценарием транзакции (Transaction Script), особенно если смущает постоянное повторение одного и того же кода и сложность обновления таблиц и сценариев; подобные ситуации нередко сопровождают использование сценариев транзакции. В этом случае вы можете приступить к созданию активных записей, постепенно перенося в них повторяющуюся логику.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]