Сдам Сам

ПОЛЕЗНОЕ


КАТЕГОРИИ







Отображение данных на поля объектов домена





Преобразователи должны иметь доступ к полям объектов домена. Зачастую это вызывает трудности, поскольку предполагает наличие методов, открытых для преобразователей, чего в бизнес-логике быть не должно. (Я исхожу из предположения, что вы несовершили страшную ошибку, оставив поля объектов домена открытыми (public).) Универсального решения этой проблемы не существует. Вы можете применить более низкийуровень видимости, поместив преобразователи "поближе" к объектам домена (например,в одном пакете, как это делается в Java), однако подобное решение крайне запутает глобальную картину зависимостей, потому что другие части системы, которые "знают" обобъектах домена, не должны "знать" о преобразователях. Вы можете использовать механизм отражения, который зачастую позволяет обойти правила видимости конкретногоязыка программирования. Это довольно медленный метод, однако он может оказатьсягораздо быстрее выполнения SQL-запроса. И наконец, вы можете использовать открытые методы, предварительно снабдив их полями состояния, генерирующими исключениепри попытке использовать эти методы не для загрузки данных преобразователем. В этомслучае назовите методы так, чтобы их по ошибке не приняли за обычные get и setметоды.

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

При использовании конструктора с инициализацией возникает проблема, связанная сналичием циклических ссылок. Если у вас есть два объекта, ссылающихся друг на друга,попытка загрузки первого объекта приведет к загрузке второго объекта, что, в свою очередь, снова приведет к загрузке первого объекта и так до тех пор, пока не произойдет переполнение стека. Возможный выход— описать частный случай (Special Case).Обычно это делается с использованием типового решения загрузка по требованию. Написание кода для частного случая - задача далеко не из легких, поэтому рекомендую попробовать что-нибудь другое, например воспользоваться конструктором без аргументовдля создания пустого объекта (empty object). Создайте пустой объект и сразу же поместитеего в коллекцию объектов. Теперь, если загружаемые объекты окажутся связанными циклической ссылкой, коллекция объектов возвратит нужное значение для прекращения"рекурсивной" загрузки.



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

Отображения на основе метаданных

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

Применимость

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

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

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

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

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

Функциональные проблемы

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

Обсудив основные варианты архитектурных решений, рассмотрим функциональную (поведенческую) сторону, в частности вопрос о том, как обеспечить загрузку различных объектов и сохранение их в базе данных. На первый взгляд это не кажется слишком сложной задачей: объект можно снабдить соответствующими методами загрузки ("load") и сохранения ("save"). Именно такой путь целесообразно избрать, например, при использовании решения активная запись (Active Record). Загружая в память большое количество объектов и модифицируя их, система должна следить за тем, какие объекты подверглись изменению, и гарантировать сохранение их содержимого в базе данных. Если речь идет всего о нескольких записях, это просто. Но по мере увеличения числа объектов растут и проблемы: как быть, скажем, в такой далеко не самой сложной ситуации, когда необходимо создать записи, которые должны ссылаться на ключевые значения друг друга.

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

Типовым решением, имеющим существенное значение для преодоления такого рода проблем, является единица работы (Unit of Work), использование которой позволяет отследить, какие объекты считываются и какие модифицируются, и обслужить операции обновления содержимого базы данных. Автору прикладной программы нет нужды явно вызывать методы сохранения — достаточно сообщить объекту единица работы о необходимости фиксации (commit) результатов в базе данных. Типовое решение единица работы упорядочивает все функции по взаимодействию с базой данных и сосредоточивает в одном месте сложную логику фиксации. Его лучшие качества проявляются именно тогда, когда интерфейс между приложением и базой данных становится особенно запутанным.

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

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

Считывание данных

Рассматривая проблему считывания информации из базы данных, рекомегдуется трактовать предназначенные для этого методы в виде функций поиска (finders), скрывающих посредством соответствующих входных интерфейсов SQL-выражения формата "select". Примерами подобных методов могут служить find (id) или findForCustomer (customer). Разумеется, если ваше приложение оперирует тремя десятками выражений "select" с различными критериями выбора, указанная схема становится чересчур громоздкой, но такие ситуации, к счастью, редки. Принадлежность методов зависит от вида используемого интерфейсного типового решения. Если каждый класс, обеспечивающий взаимодействие с базой данных, привязан к определенной таблице, в его состав наряду с методами вставки и замены уместно включить и методы поиска. Если же объект класса соответствует отдельной записи данных, требуется иной подход. В этом случае можно попробовать сделать методы поиска статическими, но за это придется заплатить некоторой долей гибкости, в частности вам более не удастся в целях тестирования заменить базу данных фиктивной службой (Service Stub). Чтобы избежать подобных проблем, лучше предусмотреть специальные классы поиска, включив в состав каждого из них методы, обеспечивающие инкапсуляцию тех или иных SQL-запросов. В результате выполнения запроса метод возвращает коллекцию объектов, соответствующих определенным записям данных.

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

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

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

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

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









Не нашли то, что искали? Воспользуйтесь поиском гугл на сайте:


©2015- 2018 zdamsam.ru Размещенные материалы защищены законодательством РФ.