Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Вопрос по паттерну Адаптер

590
04 января 2009 года
Gigahard
223 / / 03.04.2006
Хотелось бы разобраться в такой вот ситуации.
Имеется некий класс, назовем его Запрос, который может обращаться к разным типам источников данных, но используя при этом одни и те же методы читать() и писать(). Соответственно для этого создается некий абстрактный класс Адаптер в котором так же присутствуют методы читать() и писать(). От этого абстрактного класса создаются потомки в которых методы читать() и писать() уже имеют реализацию для конкретного источника данных.
По сути это паттерн Адаптер.

Возникает вот какой вопрос. В ходе выполнения программы, в зависимости от типа источника данных мы создаем нужного наследника класса Адаптер. Но каким образом это делать?
Создавать т.н. фабрику объектов и передавать ей в качестве параметра тип источника данных?
Размещать эту фабрику как отдельный объект или как часть объекта :Запрос?
При расширении числа потомков класса Адаптер каждый раз переписывать код фабрики?
Или можно каким то образом исключить объект фабрика и уйти от первоначальной зависимости от предопределенного числа потомков Адаптера?
3
04 января 2009 года
Green
4.8K / / 20.01.2000
Цитата: Gigahard

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


Да.

Цитата: Gigahard

Размещать эту фабрику как отдельный объект или как часть объекта :Запрос?


Пусть фабрика живет сама по себе.

Цитата: Gigahard

При расширении числа потомков класса Адаптер каждый раз переписывать код фабрики? Или можно каким то образом исключить объект фабрика и уйти от первоначальной зависимости от предопределенного числа потомков Адаптера?


Не надо ничего переписывать.
Сделай регистрацию классов в фабрике. Каждый новый адаптер (потомок класса Адаптера) должен быть зарегистрирован автоматически или "вручную" (я бы предпочел "вручную", т.е. явно) в фабрике. Далее при запросе адаптера определенного типа фабрика отыскивает (перебором всех зарегистрированных или с помощью ассоциативного массива) необходимый класс адаптера и возвращает пользователю его экземпляр.
Регистрацию классов можно проделать с помощью введения в них статического метода create (фабричный метод), который будет создавать экземпляр данного класса, а в фабрике храни указатели на эти методы в ассоциативном контейнере в соответствии с типом. Регистрация в таком случае сводится к помещению указателя на метод create нового класса в этот контейнер.
Если адаптеров немного, то можно обойтись без ассоциативного контейнера, используя обычный (например, список). При этом соответствие требуемому типу можно производить в процессе перебора всех зарегистрированных адаптеров.

11K
26 января 2009 года
Babandr
76 / / 05.05.2008
Цитата: Gigahard
Хотелось бы разобраться в такой вот ситуации.
Имеется некий класс, назовем его Запрос, который может обращаться к разным типам источников данных, но используя при этом одни и те же методы читать() и писать(). Соответственно для этого создается некий абстрактный класс Адаптер в котором так же присутствуют методы читать() и писать(). От этого абстрактного класса создаются потомки в которых методы читать() и писать() уже имеют реализацию для конкретного источника данных.
По сути это паттерн Адаптер.



По-моему, автор темы неверно понимает суть паттерна Адаптер. Ведь его назначение - адаптировать интерфейс одного класса к другому в том случае, когда адаптируемый класс не обеспечивает нужной внутренней функциональности.
В данном же случае можно всего лишь сделать методы читать() и писать() виртуальными и переопределять их в потомках от класса Запрос для обеспечения доступа к различным типам данных.

590
31 января 2009 года
Gigahard
223 / / 03.04.2006
Задача паттерна "Адаптер" состоит в том, чтобы предоставить классу клиенту унифицированный интерфейс не зависящий от интерфейса(ов) другого класса(ов), другой системы. Т.е. обеспечить отделение интерфейса от реализации.

Наследование запрашивающего класса как раз наоборот, является смешиванием реализации.

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

Класс инициирующий запрос не обязан ничего знать о типе источника данных. Он просто должен выполнять свои функции, обращаясь к абстрактному источнику, который уже в свою очередь и реализует взаимодействие на необходимом уровне.
Проще говоря, часть системы реализующей логику не завязана на другую часть системы занимающуюся хранением и выборкой данных.
1.9K
31 января 2009 года
GreenRiver
451 / / 20.07.2008
Цитата: Gigahard

Наследование запрашивающего класса как раз наоборот, является смешиванием реализации.


Отнюдь, ваша система будет пользоваться интерфейсом класса Запрос, ничего не зная о конкретной реализации, которую возвращает фабрика (например, MSSQLЗапрос, MySQLЗапрос и т.д.)
Это будет называться, конечно уже не Адаптор, но смысла создание класса, который только и делает, что вызывает другой класс, я не вижу... Если класс Запрос нагружен какой-то иной деятельностью (которую я не могу даже представить) тогда другое дело :)

Цитата: Gigahard

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


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

Цитата: Gigahard

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


Это конечно все правильно, но применение Адаптор + Фабрика здесь не оправданно... Можно использовать только фабрику, которая возвращает интерфейс для чтения/записи...

Адаптор нужен когда интерфейс у "внешней" системы не удовлетворяет требованиям... В Вашем случае интерфейсы совпадают и необходимости в Адаптере нет.

87
31 января 2009 года
Kogrom
2.7K / / 02.02.2008
Цитата: Gigahard
Мне совершенно не интересно плодить потомков класса инициирующего запрос, только для того, чтобы адаптировать его под новый тип источника данных. Как раз задача состоит в том, чтоб уйти от этого не затрагивая класс. И этим занимается адаптер.


А зачем тогда в диаграмме наплодил, раз не интересно? Зачем тогда нужны потомки класса Запрос? Или в той диаграмме приведены 2 альтернативы?

5
31 января 2009 года
hardcase
4.5K / / 09.08.2005
Цитата: GreenRiver
Отнюдь, ваша система будет пользоваться интерфейсом класса Запрос, ничего не зная о конкретной реализации, которую возвращает фабрика (например, MSSQLЗапрос, MySQLЗапрос и т.д.)
Это будет называться, конечно уже не Адаптор, но смысла создание класса, который только и делает, что вызывает другой класс, я не вижу... Если класс Запрос нагружен какой-то иной деятельностью (которую я не могу даже представить) тогда другое дело :)

К примеру, запрос может сортировать набор данных на клиенте. Или скрывать/добавлять дополнительные столбцы. Или элементарно дополнительно фильтровать строки. А еще можно кэшировать. Вот вам и "фантастический" функционал.

Цитата: GreenRiver
Что мешает сразу вызывать методы Адаптор.Читать, Адаптор.Писать... Вместо того, чтобы вызывать Запрос.Читать, который в свою очередь вызовет Адаптор.Читать...

Тем, что реализация класса Запрос у нас не зависит от источника данных - Адаптера, тем самым добавляет так нужный нам для реализации универсального Запроса уровень абстракции. Запрос работает с абстрактным предком Адаптер. И выполняет дополнительную логику (см выше)


Цитата: GreenRiver
Адаптор нужен когда интерфейс у "внешней" системы не удовлетворяет требованиям... В Вашем случае интерфейсы совпадают и необходимости в Адаптере нет.

Учитывая умозрительную специфику класса Запрос необходимость в Адаптере безусловно есть.

1.9K
31 января 2009 года
GreenRiver
451 / / 20.07.2008
Цитата: hardcase
К примеру, запрос может сортировать набор данных на клиенте. Или скрывать/добавлять дополнительные столбцы. Или элементарно дополнительно фильтровать строки. А еще можно кэшировать. Вот вам и "фантастический" функционал.


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

Цитата: hardcase

Тем, что реализация класса Запрос у нас не зависит от источника данных - Адаптера, тем самым добавляет так нужный нам для реализации универсального Запроса уровень абстракции. Запрос работает с абстрактным предком Адаптер. И выполняет дополнительную логику (см выше)


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

P.S. Но даже если принять такую схему компоновки системы, все равно здесь даже не пахнет паттерном Адаптер - который есть лишь способ привести один интерфейс к другому... никакой иной функции у него нет.

341
31 января 2009 года
Der Meister
874 / / 21.12.2007
[QUOTE=GreenRiver]А вам не кажется, что этим должен не запрос заниматься, а некий DataSet или подобное...[/QUOTE]Отнюдь. Табличное представение данных ущербно изначально.[QUOTE=GreenRiver]Но даже если принять такую схему компоновки системы, все равно здесь даже не пахнет паттерном Адаптер - который есть лишь способ привести один интерфейс к другому... никакой иной функции у него нет.[/QUOTE]От лишь бы...
5
31 января 2009 года
hardcase
4.5K / / 09.08.2005
Цитата: GreenRiver
P.S. Но даже если принять такую схему компоновки системы, все равно здесь даже не пахнет паттерном Адаптер - который есть лишь способ привести один интерфейс к другому... никакой иной функции у него нет.


Сдается мне, вы путаете Адаптер и Декоратор (фасад). Обратитесь к Википедии, к примеру, там это отличие продемонстрировано.

63
01 февраля 2009 года
Zorkus
2.6K / / 04.11.2006
Цитата: hardcase
Сдается мне, вы путаете Адаптер и Декоратор (фасад). Обратитесь к Википедии, к примеру, там это отличие продемонстрировано.


Фасад логически ближе к адаптеру, чем к декоратору (точней, с декоратором он вообще не связан).
Если адаптер позволяет клиенту рассматривать класс, не реализующий некоторый интерфейс, то фасад позволяют рассматривать группу произвольных классов как единый интерфейс (объект с заданным интерфейсов).
В википедии написано, что это близкие паттерны (+ замечание):
[QUOTE=Примечание в википедии на странице http://ru.wikipedia.org/wiki//Адаптер_(шаблон_проектирования)] Разница состоит в том, что шаблон Фасад предназначен для упрощения интерфейса, тогда как шаблон Адаптер предназначен для приведения различных существующих интерфейсов к единому требуемому виду.[/QUOTE]
Но оно же подчеркивает и сходство этих паттернов.

341
01 февраля 2009 года
Der Meister
874 / / 21.12.2007
Схема может быть как адаптером, так и маппером, шлюзом или вовсе активной записью... Если автор применяет схему как адаптер, то пусть она будет адаптером. Вне контекста использования с ходу и однозначно заявить, что вписывается в выбранную терминологию, а что нет, не представляется возможным, и тем более стоящим того, чтобы расставлять столь "многозначительные" акценты. Автора поняли воплне однозначно, что главное.
1.9K
02 февраля 2009 года
GreenRiver
451 / / 20.07.2008
В общем-то по сабжу никаких комментариев нету, дискуссия по поводу последнего поста топик-стартера >>
Кстати пока неизвестна область применения сей конструкции спорить особо бесплозно... Потому, что Запрос действительно может нести какие-то функции - и тогда схема имеет место быть!
Но я очень сомневаюсь, что класс Запрос чем-нибудь нагружен... простите мне мое упрямство :)

P.S. to Der Meister: Кстати, что значит "Табличное представение данных ущербно изначально"?
341
03 февраля 2009 года
Der Meister
874 / / 21.12.2007
[QUOTE=GreenRiver]В общем-то по сабжу никаких комментариев нету, дискуссия по поводу последнего поста топик-стартера[/QUOTE]Автор расслаивает систему. К чему возражения? Его высказывания относительно применения схемы не противоречат смысловой нагрузке адаптера, всё у него там должно быть нормально.[QUOTE=GreenRiver]Кстати, что значит "Табличное представение данных ущербно изначально"?[/QUOTE]Поверьте, если бы автор мог обойтись дейтасетом, он не стал бы городить огород абстрактных запросов :)
1.9K
03 февраля 2009 года
GreenRiver
451 / / 20.07.2008
Цитата: Der Meister
...всё у него там должно быть нормально...


Я только "за" :)

590
04 февраля 2009 года
Gigahard
223 / / 03.04.2006
Дальше пошел непродуктивный разговор ради разговора. Всю необходимую мне на данный момент информацию я выяснил в первых постах. Спасибо сем, кто принял участие :)
Единственное хочу ответить Kogrom что на диаграмме я изобразил два варианта. Первый тот о чем я говорил, а второй тот, о котором говорил Babandr.

GreenRiver
Если класс Запрос нагружен какой-то иной деятельностью...
Само собой разумеется. Подача запроса отнюдь не единственная функция класса. Просто рассматривать остально в данном контексте я не вижу надобности. Мы ж об абстрации сейчас говорим.

В Вашем случае интерфейсы совпадают и необходимости в Адаптере нет.
Не совпадают. Парсинг DOM XML документа и выборка из БД - шибко разные интерфейсы... К тому же, с прицелом на будущее, добавление новых вариантов.

В принципе hardcase сабж в последствии хорошо "разжевал".


P.S. вопрос не в том, применять не применять и уместно не уместно, а про детали реализации был... Собственно весь вопрос был в добавлении (регистрации) нового потомка адаптера, так чтобы не переписывать в результате код фабрики.

P.P.S Фасад объединяет интерфейсы разнородных систем в один предоставляемый клиенту. В этом плане он хоть и похож на Адаптер, но при этом отличается тем, что реализует все в одном единственном классе.
Т.е. если Адаптер представляет модульность, используя полиморфизм и реализуя логику ИЛИ, то фасад объединяет интерфейсы сводя их к одному, используя логику И.
1.9K
04 февраля 2009 года
GreenRiver
451 / / 20.07.2008
Цитата: Gigahard

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


А какие ещё функции у Запроса? Кстати какой язык? PHP?

Цитата: Gigahard

P.S. вопрос не в том, применять не применять и уместно не уместно, а про детали реализации был... Собственно весь вопрос был в добавлении (регистрации) нового потомка адаптера, так чтобы не переписывать в результате код фабрики.


Все правильно. А потом Вы сказали, что если наследовать от Запроса, то произойдет смешивание реализаций... и понеслось :)

Цитата: Gigahard

P.P.S Фасад объединяет интерфейсы разнородных систем в один предоставляемый клиенту. В этом плане он хоть и похож на Адаптер, но при этом отличается тем, что реализует все в одном единственном классе.
Т.е. если Адаптер представляет модульность, используя полиморфизм и реализуя логику ИЛИ, то фасад объединяет интерфейсы сводя их к одному, используя логику И.


Ёлки-палки... почитайте Википедию, или здесь, или прислушайтесь к тому, что говорит Zorkus.

341
04 февраля 2009 года
Der Meister
874 / / 21.12.2007
2GreenRiver
Опять. Вы щас добиваетесь чего?
Запрос делегирует адаптеру, адаптер делегирует соответствующему API. Простейший пример:
Код:
class Person
{
    string _Name;

    class PersonQuery : Query<Person>
    {
        public PersonQuery(Adapter adapter) : base(adapter) { }
        publicPerson FindById(long id) { ... }
    }
    // класс Query<T> имеет конструктор Query(Adapter adapter) и,
    // помимо всего прочего, кеширует уже извлечённые из источника
    // данных экземпляры класса T

}

class Person
{
    class XMLPersonQuery : XMLQuery
    {
         Person FindByID(long id);
    }

    class SQLPersonQuery : SQLQuery
    {
         Person FindByID(long id);
    }
}
Разницу видите? Adapter приводит соответствующий API к единому интерфейсу. Один из методов сего интерфейса мог бы выглядеть, например, так:
 
Код:
class Adapter...
    object[] Read(string objClass, long id);

Мне просто непонятны мотивы продолжения вами дискуссии в этом русле.
1.9K
04 февраля 2009 года
GreenRiver
451 / / 20.07.2008
Цитата: Der Meister

Мне просто непонятны мотивы продолжения вами дискуссии в этом русле.


Спокойствие, только спокойствие. Я на счет схемы ничего не говорю ... и с самого начала говорил, что нормальная схема, если есть нагрузка на Запрос. То, что спросил какая именно нагрузка - это чистое любопытство...

Единственно, что мне непонятно зачем придумывать какие-то ужасы, типа паттернов, когда это обычная реализация интерфейса?!
Имеется класс/интерфейс Адаптер, имеется несколько наследников, работающих с разными источниками, но реализующими интерфейс Адаптер... в чем паттерн здесь? Это все, что я хочу сказать...

341
04 февраля 2009 года
Der Meister
874 / / 21.12.2007
[QUOTE=GreenRiver]Имеется класс/интерфейс Адаптер, имеется несколько наследников, работающих с разными источниками, но реализующими интерфейс Адаптер... в чем паттерн здесь?[/QUOTE]Ну это оно и есть :) Сложность сосредоточена в классах XMLAPI, TextFileAPI и иже с ними, вкупе с процессом преобразования данных из формата домена к формату, используемому в работе с вышеперечисленными классами (и обратно). Ну и "дореализация" отсутствующего функционала. В этом и паттерн.
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог