Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > Общие вопросы по .NET и C# > Фабрика классов |
Автор: Vyacheslav 15.3.2005, 11:07 |
Если в .NET встроенные средства для реализации паттерна "Фабрика классов" |
Автор: Borisff2003 15.3.2005, 11:10 |
Наскока я знаю, нет. Посмотри здесь заготовки http://www.dotsite.spb.ru/solutions/patterns/ |
Автор: Vyacheslav 15.3.2005, 11:39 |
То что там, пример довольно примитивного варианта фабрики, причем в его стандартном исполнении. А где же использование возможностей Атрибутов и Отражения? |
Автор: Domestic Cat 15.3.2005, 12:10 | ||
Вообще-то паттерн - это не жесткая структура, загнать к-л паттерн в конкретную реализацию и сказать что это он и есть в конечном варианте нельзя. Сам по себе паттерн писать так же бессмыссленно как пытаться отделить полиморфизм от класса. Паттерн - это совокупность связей / взаимоотношений твоих объектов, каким образом создать средства для его реализации??? И при чем тут аттрибуты и рефлекшн???? |
Автор: Vyacheslav 15.3.2005, 12:41 | ||
Ну спасибо за азы. Бестолковые, между прочим. Ну уж никак не думал, что "релизовать паттерн" воспримется как "написать паттерн".
Это то же кстати одно из определений паттерна |
Автор: [Last]Wizard 15.3.2005, 12:49 | ||
Вот именно, ничего не изобретая! Написать паттерн можно просто механически, зная, как он реализуется, не нужно думать над тем, как спроектировать классы, и пр. Ведь не зря же эти паттерны называются ПАТТЕРНАМИ ПРОЕКТИРОВАНИЯ, а не ПРОГРАММИРОВАНИЯ! |
Автор: Vyacheslav 15.3.2005, 13:27 | ||||||
Приехали. Абстрактная Фабрика - паттерн, порождающий объекты Предоставляет интерфейс для создания семейств взаимосвязанных или взаимозависимых объектов, не специфирую их конкретных классов Абстрактная Фабрика очень часто реализуется с помощью патерна Factory Method или еще его называют Virtual Constuctor В С++ Virtual Constuctor либо имитируется с помощью switch или if, что довольно примитивно и требует при добавлении нового класса вносить изменение в класс Фабрики, либо с помощью map-таблицы сожержащий некий ключ(например текстовое имя ) и указатель на функцию, создающую объект класса, соответсвующего ключу. При этом дополнительно требуется перед использованием фабоики, провести процедуру регистрации класса, но при этом фабрика соверршено становится независима от классов, объекты которых она создает. В .NET имеется куча мета-информации и вполне врзможно создание класса по параметру Что первое приходит в голову, так это примерно вот это (на первый взгляд, ибо я только в начале .NET)
Добавлено @ 13:31
Согласен, но реализация паттерна(имеется в виду дизайн) на конкретном языке ( и платформе ) может быть различна, не говоря уж о квалификации ![]() |
Автор: Vyacheslav 15.3.2005, 13:42 |
Кстати, если заменим enum на String, а при создании объекта Fabric будем передоватьимя сборки, то получаем фабрику классов, совершеено независимую от классов, которые она будет создавать Но, естесвенно, это всего лишь первый набросок, поэтому и возник вопрос. Может кто уже давно сделал лучше. |
Автор: Domestic Cat 15.3.2005, 19:22 | ||||||||||||
Гоф цитировать мне не надо, а лучше взглянуть на класс диаграмму для этого паттерна. И узнать, что все-таки у них каждая конкретная фабрика - сабкласс AbstractFactory знает о тех объектах, которые она должна произвести.
Интересно, и где это в абстрактной фабрике свичи, ифы или мапы? Хотя бы на пример в Гамме посмотри...
Ничего особо хорошего в этом методе нет, поскольку рефлекшн - не самый быстрый способ создать класс. Никто конечно не мешает таким образом реализовать паттерн. Только вот он никоим образом не будет подходить всем и вся. Потому, что основная мотивация абстрактной фабрики (смотреть опять-таки ГоФ)
Заметь - нигде не сказано, что фабрика не должна о чем-то знать. СИСТЕМА не должна знать. Главная цель - иметь возможность легко создавать группы взаимозаменяемых объектов, причем с минимальным вмешательством в систему. И способы, описанные в Дизайн Паттернах хороши и тем, что они служат поставленной задаче, и своей простотой. И лично я не вижу смысла в создании фабрики, которая не знает, чего она производит. Потому что в лучшем случае это даст выигрыш в пару строк кода, но зато создаст серьезный оверхед из-за рефлекшна. Возможно, ее потом и можно реюзать, но придется возиться с рефлекшном.
Если тебя не поняли - это отчасти и твоя вина. В английском техническом языке "to realize" означает "make real or concrete; give reality or substance to; "our ideas must be substantiated into actions"". Весьма странные слова для модератора. |
Автор: [Last]Wizard 15.3.2005, 19:43 | ||
Vyacheslav, если тебе нужна фабрика, которая
то посмотри Activator.CreateInstance(). Хотя до паттерна "Абстрактная фабрика" это не дотягивает. |
Автор: Vyacheslav 15.3.2005, 20:07 | ||||||
То что одним из возможных вариантов реализации Абстрактной Фабрики явлется реализация посредством Фабричного Метода - это, надеюсь возражений не вызывает? Если вызывает- отсылаю к Гамму. Смотрим Фабричный метод. Там используется класс Creator, который может содержать параметризованный фабричный метод Так если Вы заглянете в книжку, то увидидите то, что в реализации для этого метода на С++ используется конструкция if.
![]() Это во первых. А во вторых, надо понимать, что там даны всего лишь примеры для иллюстрации того, как реализация паттерна будет выглядеть на конкретном языке и для определенного примера. К тому же качество кода в примерах весьма посредственное, но другого там и не требовалось. Ибо цель примеров - донести идею, а не научить программировать. И естественно для С++ это можно реализовать более удачно http://forum.vingrad.ru/index.php?showtopic=9713&view=findpost&p=60738
Ок. В таком случае подскажите, как я должен был сформулировать вопрос, что бы Вы не поняли что собирался не описывать паттерн, а используя уже имеющиеся описания, "реализовать"(извините другого слова подобрать не смог) его применительно к решению конкретной задачи и конкретной языковой среды? |
Автор: Domestic Cat 15.3.2005, 20:17 | ||||
Вызывает. это другой паттерн, никак не абстрактная фабрика.
это зависит от задачи, а о конкретной задаче пока разговора не было. Если действительнo в runtime неизвестно, объекты каких классов надo создать, тo да, такой вариант имеет место быть. Хотя oбъект можнo создавать не по аттрибуту, а по типу или стринговому названию класса - как подсказал [Last]Wizard. http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemactivatorclasscreateinstancetopic.asp Так прощe. |
Автор: Vyacheslav 15.3.2005, 20:18 | ||||
Спасибо. Это как раз первое, за что я зацепился. Это естественно не фабрика, другое дело - это, по всей видимости, можно эффективно использовать при создании фабрики |
Автор: Vyacheslav 15.3.2005, 20:43 | ||||||||
[/code]
Дедо в том, что завязка непосредственно на тип просто перемещает if'ы (или что то вместо них) в другое место. Мне приходит откуда-то некая информация , по которой я должен созлать экземпляр класса. Соответсвенно мне все равно придется где то иметь код, который доджен сопоставить значение этой информации с типом. Естественно хочется, что бы при добавлении нового класса, не требоавалось лезть туда и снова что то добавлять. В идеале , нужно сделать так, что бы вообще не требовалась перекомпиляция основной приложения. Достаточно было бы добавить класса в библиотеку (в дополнительную сборку). Во всяком случае такой код уже работает Требуемый экземпляр класса создается на основании значения свойства MsgType класса CMessage
При необходимости добавить еще одни класс(CTestExecutor)например достаточно добавить
В резулбтате приминительно к моему случаю обработка приходящих сообщений выливается в
|
Автор: Domestic Cat 15.3.2005, 20:52 |
Ну в такой ситуации - я согласен. Если б сначала написал о задаче, и разговора б не было ![]() |
Автор: Vyacheslav 16.3.2005, 10:36 | ||||
Возможно. То что я реализовал - это первое, что пришло мне в голову при поверхностном обзоре возможностей .NET. А я в нем только с начала марта. А я по опыту знаю, что первое пришедшее решение далеко не всегда эффективно. С другой стороны иммитировать виртуальный конструктор средствами pure C++ при наличии такового( CreateInstance) в среде выглядит довольно глупо. Тем более, IMHO то что написанное на чистом С++ и STL выглядит изящнее, чем его эквивалент на managed C++. Отсюда и возник первоначальный вопрос Добавлено @ 10:44 Вот чертов managed C++ Чтобы в pure С++ закешировать список типов мне достаточно было указать static Вместо
использовать
но в managed C++ это не проходит. Ругается компилятор. Теперь придется что то изобретать дополнительно. |
Автор: Vyacheslav 16.3.2005, 11:07 | ||
Во как получилось. В полном противоречии со стандартом С++ ![]()
|
Автор: arilou 16.3.2005, 20:29 | ||
Честно говоря, просмотрев эту ветку, у меня возник немного другой вариант. Напишу на C#, т.к. мне это ближе. Для моей реализации необходимо 2 допущения: 1) Каждому классу, наследующемуся из MyApp.MyNamespace.MyMessage соответствует класс MyMessageDescendantExecutor, где MyMessageDescendant - это имя класса-потомка MyMessage. Класс MyMessageDescendantExecutor должен находиться в том же namespace, что и MyMessageDescendant. 2) Все Executor'ы соответствуют интерфейсу IExecutor
В результате велосипед не изобретен, пользуемся стандартными средствами. Как написано в комментарии, можно кэшировать не типы executor'ов, а инстансы. Надеюсь, что это имеет смысл. Таким же образом решаются задачи этого уровня. P.S. 1) Не заметил, писал ли кто-нить, что Activator.CreateInstance в сочетании с информацией о типах реализует абстракную фабрику классов, т.е. такую, которая умеет создавать инстансы любых типов. 2) Добавление других сообщений и executor'ов происходит с минимальными издержками - добавляешь 2 класса, и вуаля - все работает ![]() |
Автор: Vyacheslav 17.3.2005, 10:29 |
Тут оказалась скрыта другая часть задачи. Дело в том, что при таком подходе проблема просто перетечет в PeekIncoming(). От клиента приходит массив байтов, с помощью которого заполняется CMessage. Если использовать Ваш подход, то мне все равно на основании данных, содержащихся в массиве необходимо создать соответсвующий этим данным класс XXXMessage, а затем используя имя класса создать экземпляр класса XXXMessageExecutor. При этом все равно вопрос остается открытым : нужно приходящим данным сопоставить соответствущий класс( теперь не Executor, а Мessage) и создать его экземпляр. Здесь есть один неприятный момент: клиентская часть уже исторически унаследована, может работать на различных платформах и переделать ее таким образом, что бы она вместо линейного массива данных передавала непосредственно объект, нельзя. Интерфейс и протокол обмена определен и зафиксирован и моя программа дожна обрабатывать сообщения, поступающие от него. Проблема в том, что их в общем случае очень много, а я должен обрабатывать ограниченную часть, но никто не гарантирует, что после некоторого времени, потребуется зайдействовать дополнительные команды из этого набора. Также не исключается возможность, что добавлять обработки новых сообщения буду не я и даже не моя фирма. Поэтому и хочется по максимому облегчить процесс добавления В моем случае при этом добавляется один класс и, я пока не силен в .NET, мне кажется при внесении небольших изменений, можно дело поставить таким образом, что добавление новых классов будет производится просто посредством подмены некоторой загружаемой библиотеки (сборки?) |
Автор: arilou 17.3.2005, 13:00 | ||||||||
Наверняка массив байт содержит в начале некий заголовок, который указывает на тип сообщения. Я прав? Если так, то можно сделать следующее (в целях упрощения заголовок будет типа byte, т.е. имеем 255 различных типов сообщений):
Мой приведенный выше пример можно расширять следующим образом: 1) Можно редактировать исходный код - добавлять новые сообщения и executor'ы - это при условии, что есть доступ к исходникам 2) Выносим Message и IExecutor в отдельную общую сборку. В MessageRepository.Init заружаем все сборки из определенного каталога (например, components), перебираем все загруженные сборки, выполняем ту же процедуру (см. метод Init) для каждого типа, удовлетворяющего условию. Разработка дополнительных типов сообщений потребует от других фирм поставить референс на Вашу общую сборку (там, где определен класс Message и интерфейс IExecutor), реализовать их и поместить в каталог components, откуда ваша программа благополучно считает их в repository. Наедюсь, что это Вам поможет. ![]() P.S. Кстати, таким же образом можно реализовать и загрузку и сопоставление всех Executor'ов. P.P.S Таким образом, функция PeekIncoming может выглядеть следующим образом:
|
Автор: Vyacheslav 17.3.2005, 14:08 | ||||
Насчет типа сообщения Вы правы. Только сначала идет длина, потом тип. Насчет же всего прочего, я примерно так и делал за некоторым исключением. Во первых я таким образом создавал не Message, а Executor
Во-вторых, основное отличие, как я понял в том, что бы не перебирать каждый раз все типы сборки, а предварительно закэшировать в Hashtable только те типы, которые впоследствии потребуются. Это в высшей степени разумно. Я обязательно воспользуюсь этим. А теперь вопрос. Можно ли заменить в принципе это десериализацией при условии при фиксированном и неизменном формате присылаемых данных? Ведь приведенный вами код в принципе этим и является
|
Автор: arilou 17.3.2005, 14:17 | ||
В принципе
Не могу придумать, чем тут поможет десериализция в том понятии, в котором она используется в .NET'e, ведь приходит некий поток байтов, который же не является бинарным отображением объекта? Если бы это были какие-нить символьные данные, то можно было бы через XSL превращать их в XML-сериализованное представление объекта, и десериализовать сам объект. На выходе получался бы экземпляр Message. Скорее, я бы определил этот метод (ReadFromStream) в базовом классе, а в потомках бы из байтового массива на входе доставал бы данные и записывал в поля и свойства. Не зная специфики принимаемых данных ничего более умного предложить не могу ![]() |
Автор: Vyacheslav 17.3.2005, 14:53 |
Я не стал использовать метод. Просто в конструктор тупо передаю массив и от хранится как поле класса. Для общих свойств(тип, длина итд) определил свойства, которые читают-записывают прямо из массива. |
Автор: arilou 17.3.2005, 14:59 | ||
Логично ![]() |
Автор: Vyacheslav 17.3.2005, 16:10 | ||
Я это как раз и делаю ![]()
Меня радует, что после обсуждения мы пришли почти к единому знаменателю. А значит можно сказать, что решение с большой долей вероятности работоспособно. В принципе моя цель в этом и заключалась: убедится в применимомти или непригодности решения |
Автор: arilou 17.3.2005, 16:18 |
Ну тогда топик действительно можно закрывать ![]() |