#java #design-patterns #dao
#java #шаблоны проектирования #dao
Вопрос:
Я освежал свои шаблоны проектирования и наткнулся на мысль, на которую нигде не смог найти хорошего ответа. Так что, может быть, кто-нибудь с большим опытом сможет мне помочь.
Предназначен ли шаблон DAO только для доступа к данным в базе данных?
Большинство ответов, которые я нашел, подразумевают «да»; на самом деле большинство из тех, кто говорит или пишет по шаблону DAO, как правило, автоматически предполагают, что вы работаете с какой-то базой данных.
Однако я не согласен. У меня мог бы быть DAO, подобный следующему:
public interface CountryData {
public List<Country> getByCriteria(Criteria criteria);
}
public final class SQLCountryData implements CountryData {
public List<Country> getByCriteria(Criteria criteria) {
// Get From SQL Database.
}
}
public final class GraphCountryData implements CountryData {
public List<Country> getByCriteria(Criteria criteria) {
// Get From an Injected In-Memory Graph Data Structure.
}
}
Здесь у меня есть интерфейс DAO и 2 реализации, одна из которых работает с базой данных SQL, а другая, которая работает, скажем, со структурой данных в графе памяти. Это правильно? Или реализация graph предназначена для создания в каком-то другом типе слоя?
И если это правильно, каков наилучший способ абстрагировать конкретные детали реализации, которые требуются для каждой реализации DAO?
Например, возьмем приведенный выше класс критериев I. Предположим , что это так:
public final class Criteria {
private String countryName;
public String getCountryName() {
return this.countryName;
}
public void setCountryName(String countryName) {
this.countryName = countryName;
}
}
Для SQLCountryData ему необходимо каким-то образом сопоставить свойство countryName с идентификатором SQL, чтобы он мог генерировать правильный SQL. Для GraphCountryData, возможно, необходимо создать какой-то объект-предикат для свойства countryName, чтобы отфильтровать вершины из графика, которые терпят неудачу.
Какой наилучший способ абстрагировать подобные детали, не связывая клиентский код, работающий с абстрактными CountryData, с такими деталями, специфичными для реализации, как это?
Есть какие-нибудь мысли?
Редактировать:
Приведенный мной пример класса Criterions достаточно прост, но подумайте, хочу ли я разрешить клиенту создавать сложные критерии, где они должны указывать не только свойство для фильтрации, но также оператор равенства, логические операторы для составных критериев и значение.
Комментарии:
1. Я, например, не использую DAO только для доступа к базе данных. В моем проекте у меня часто есть DAO, который считывает данные, как следует из названия, из какого-либо источника. Иногда это база данных, в других случаях это может быть файл, определенный поток, устройство шифрования, аппаратный генератор случайных чисел, называйте как хотите. Пока он предоставляет мне данные, и мне все равно, откуда поступают данные, мне удобно следовать шаблону DAO. Это также упрощает рефакторинг кода позже, то, что было базой данных SQL, может стать каким-то другим механизмом хранения, без какого-либо влияния на мое приложение, кроме как на реализацию DAO.
Ответ №1:
DAO являются частью DAL (уровень доступа к данным), и вы можете иметь данные, поддерживаемые любым видом реализации (XML, RDBMS и т.д.). Вам просто нужно убедиться, что экземпляр проекта введен / используется во время выполнения. В этом случае используются DI-фреймворки, такие как Spring / Guice. Кроме того, ваш Criteria
интерфейс / реализация должны быть достаточно общими, чтобы фиксировались только бизнес-детали (т. Е. критерии названия страны), а фактическое сопоставление снова обрабатывалось классом реализации.
В вашем случае для SQL вы можете вручную сгенерировать SQL, сгенерировать его с помощью вспомогательной библиотеки, такой как Spring, или использовать полноценный фреймворк, такой как MyBatis. В нашем проекте файлы конфигурации Spring XML использовались для разделения клиента и реализации; в вашем случае это может отличаться.
РЕДАКТИРОВАТЬ: Я вижу, что вы подняли аналогичную проблему в предыдущем вопросе. Ответ по-прежнему остается тем же. Вы можете добавить в свой интерфейс столько гибкости, сколько захотите; вам просто нужно убедиться, что реализация достаточно умна, чтобы понимать все аргументы, которые она получает, и соответствующим образом сопоставлять их с базовым источником. В нашем случае мы извлекли объект value с бизнес-уровня и преобразовали его в map на уровне реализации SQL, который может быть использован MyBatis. Опять же, этот процесс был в значительной степени прозрачным, и единственным способом для сервисного уровня взаимодействовать с DAO был через объекты значений, определенные интерфейсом.
Комментарии:
1. Небольшая мысль, что, если бы вы хотели, чтобы клиент определил свойство для заказа, например. Как бы объект Criteria соответствовал этому, чтобы вы могли перевести его, скажем, в map для передачи MyBatis? Извините, если это кажется похожим вопросом, поэтому мой последний; Я признаю, что они отчасти похожи
2. Я бы передал вместе с критериями (например, названием страны) и значением заказа (объект, имеющий имя поля и тип заказа
ASC
илиDESC
). MyBatis способен создавать динамические запросы на основе переданного в «value object» или «map», которые должны обрабатывать созданный запрос.3. Я предполагаю, что вы имеете в виду, что вы передали бы имя поля в базе данных? Или вы имеете в виду имя «свойства» в модели значений, которую вы хотите упорядочить, и в вашей карте SQL вы бы перевели его в подходящее имя поля dtabase? Если последнее, не будет ли тонны условной логики, если в модели так много «свойств»?
4. Я имел в виду имя столбца базы данных, которое можно использовать в динамическом запросе вместе с направлением (
ASC
,DESC
). В вашей реализации SQL DAO вы прочитаете эти значения и создадите карту, которая будет представлять собой что-то вроде;map.put("sortColumn", ordering.column); map.put("sortOrder", ordering.order); map.put("countryName", myCountryName);
. Затем эта карта будет передана MyBatis, и XML MyBatis будет использовать «ключи» карты — это свойства при создании запросов.5. Верно, но это именно моя точка зрения. Если клиентский код работает с внедренным экземпляром вашего DAO, он работает с интерфейсом, а не с конкретной реализацией. Так что, по сути, он понятия не имеет, против какой реализации он работает. Это может быть SQL или Graph, как в моих примерах. Таким образом, если вы заставляете вызывающего устанавливать критерии заказа, используя имя поля базы данных, когда они вызывают метод DAO, там есть неявная связь, и вся идея интерфейса бесполезна. Понимаете, о чем я говорю?
Ответ №2:
Нет, я не верю, что это привязано к только базам данных. Это сокращение означает Data Access Object
, а не «Объект доступа к базе данных», поэтому его можно использовать с любым типом источника данных.
Весь смысл этого заключается в том, чтобы отделить приложение от резервного хранилища данных, чтобы хранилище можно было изменять по желанию, при условии, что оно по-прежнему следует тем же правилам.
Это не просто означает переход на Oracle и установку DB2. Это также может означать переход на решение, полностью не основанное на СУБД.
Ответ №3:
хорошо, это немного философский вопрос, поэтому я расскажу, что я думаю по этому поводу. DAO обычно расшифровывается как объект доступа к данным. Здесь источником данных не всегда является база данных, хотя в реальном мире реализации обычно сводятся к этому. Это может быть XML, текстовый файл, какая-либо удаленная система или, как вы указали, график объектов в памяти.
Из того, что я видел в реальном проекте, да, вы правы, вы должны предоставлять разные реализации DAO для доступа к данным разными способами. В этом случае один dao переходит в DB, а другая реализация dao переходит в object graph.
Интерфейс DAO должен быть разработан очень тщательно. Ваши «Критерии» должны быть достаточно общими, чтобы инкапсулировать способ, которым вы собираетесь получать данные. Как достичь такого уровня развязки? Ответ может варьироваться в зависимости от вашей системы, в общем, я бы сказал, что ответ будет «как обычно, путем добавления другого уровня косвенности» 🙂
Вы также можете представить свой объект criteria как объект данных, где вы предоставляете только данные, необходимые для запроса. В этом случае вам даже не нужно будет поддерживать разные критерии. Каждая конкретная реализация DAO будет принимать эти данные и обрабатывать их по-своему: один будет создавать запрос для графика, другой будет привязывать это к вашему SQL.
Чтобы свести к минимуму проблемы с обслуживанием, я бы посоветовал вам использовать фреймворки управления зависимостями (например, Spring). Обычно эти фреймворки хорошо подходят для создания экземпляров ваших объектов DAO и хорошо работают вместе. Удачи!
Комментарии:
1. Интересно. Не могли бы вы дать небольшое объяснение, что вы подразумеваете под «другим уровнем косвенности»? Я пытаюсь придумать способ создания объекта Criteria, как вы сказали, где клиентский код может указывать, какое свойство фильтровать, его оператор (=, > и т.д.) и значение общим несвязанным образом, и каждая реализация DAO может интерпретировать его так, как ей нравится. Но, похоже, я не могу в этом разобраться. РЕДАКТИРОВАТЬ: Спасибо за отличный подробный ответ, кстати.
Ответ №4:
Нет, DAO только для баз данных — распространенное заблуждение.
DAO — это «Объект доступа к данным», а не «Объект доступа к базе данных». Следовательно, везде, где вам нужно загружать данные в / из (например, в файл, память, базу данных и т.д.), Вы можете использовать DAO.
В доменно-ориентированном дизайне существует шаблон репозитория. Хотя Repository
слово намного лучше, чем три случайные буквы (DAO), концепция та же.
Цель шаблона DAO / Repository — абстрагировать резервное хранилище данных, которым может быть все, что может содержать состояние.