Модераторы: Partizan, gambit

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Фабрика классов, C++.NET 
:(
    Опции темы
Vyacheslav
Дата 15.3.2005, 11:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 2124
Регистрация: 25.3.2002
Где: Москва

Репутация: 2
Всего: 59



Если в .NET встроенные средства для реализации паттерна "Фабрика классов"


--------------------
С уважением, Вячеслав Ермолаев
PM MAIL WWW ICQ   Вверх
Borisff2003
Дата 15.3.2005, 11:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 198
Регистрация: 26.2.2004
Где: г. Уфа

Репутация: нет
Всего: 1



Наскока я знаю, нет.
Посмотри здесь заготовки
http://www.dotsite.spb.ru/solutions/patterns/
--------------------
Лень, двигатель прогресса
PM MAIL WWW ICQ   Вверх
Vyacheslav
Дата 15.3.2005, 11:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 2124
Регистрация: 25.3.2002
Где: Москва

Репутация: 2
Всего: 59



То что там, пример довольно примитивного варианта фабрики, причем в его стандартном исполнении. А где же использование возможностей Атрибутов и Отражения?


--------------------
С уважением, Вячеслав Ермолаев
PM MAIL WWW ICQ   Вверх
Domestic Cat
Дата 15.3.2005, 12:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5452
Регистрация: 3.5.2004
Где: Dallas, US

Репутация: 9
Всего: 172



Цитата(Vyacheslav @ 15.3.2005, 02:39)
То что там, пример довольно примитивного варианта фабрики, причем в его стандартном исполнении. А где же использование возможностей Атрибутов и Отражения?


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


--------------------

PM   Вверх
Vyacheslav
Дата 15.3.2005, 12:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 2124
Регистрация: 25.3.2002
Где: Москва

Репутация: 2
Всего: 59



Ну спасибо за азы. Бестолковые, между прочим. Ну уж никак не думал, что "релизовать паттерн" воспримется как "написать паттерн".
Цитата

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

Это то же кстати одно из определений паттерна






--------------------
С уважением, Вячеслав Ермолаев
PM MAIL WWW ICQ   Вверх
[Last]Wizard
Дата 15.3.2005, 12:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 113
Регистрация: 20.7.2004
Где: Минск, Беларусь

Репутация: 4
Всего: 10



Цитата(Vyacheslav @ 15.3.2005, 11:41)
ничего не избретая заново

Вот именно, ничего не изобретая!

Написать паттерн можно просто механически, зная, как он реализуется, не нужно думать над тем, как спроектировать классы, и пр.
Ведь не зря же эти паттерны называются ПАТТЕРНАМИ ПРОЕКТИРОВАНИЯ, а не ПРОГРАММИРОВАНИЯ!
PM ICQ   Вверх
Vyacheslav
Дата 15.3.2005, 13:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 2124
Регистрация: 25.3.2002
Где: Москва

Репутация: 2
Всего: 59



Цитата(Domestic @ 15.3.2005, 12:10)
И при чем тут аттрибуты и рефлекшн????

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

Абстрактная Фабрика очень часто реализуется с помощью патерна Factory Method или еще его называют Virtual Constuctor
В С++ Virtual Constuctor либо имитируется с помощью switch или if, что довольно примитивно и требует при добавлении нового класса вносить изменение в класс Фабрики, либо с помощью
map-таблицы сожержащий некий ключ(например текстовое имя ) и указатель на функцию, создающую объект класса, соответсвующего ключу.
При этом дополнительно требуется перед использованием фабоики, провести процедуру регистрации класса, но при этом фабрика соверршено становится независима от классов, объекты которых она создает.

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

public __value enum MyClasses { First, Second, Third };

public __gs class MyClassAttr : public Attribute
{
    MyClasses   m_type;
public:
     MyClassAttr(MyClasses  type ) : m_type(type) {}
};

public __gs __abstract class  MyBaseClass
{
...
};


[ MyClassAttr (MyClasses::First)] 
public __gs __abstract class  MyFirstClass : public MyBaseClass

{
...
};

[ MyClassAttr (MyClasses::Second)] 
public __gs __abstract class  MySecondClass: public MyBaseClass

{
...
};

[ MyClassAttr (MyClasses::Third)] 
public __gs __abstract class  MyThirdClass: public MyBaseClass
{
...
};


public __gs class Fabric 
{
    Assemply* library;
    ....
    bool IsEqualAttr(MyClasses, Type)
 public:
   ....
    MyByClass* CreateMyClass(MyClasses attr);

// использовать это можно примерно вот так

MyBaseClass* Fabric::CreateMyClass(MyClasses attr)
{
      
  Type* types[] = library->GetTypes();
   for (int i = 0; i < types->Count; i++)
   {
           //ищем тип с атрибутом MyClassAttr , равным attr
 
           if (IsEqualAttr(attr,types[i] )           {
                // создаем объект  
                 return __try_cast<MyBaseClass*>(Activator::CreateInstance(types[i]))
           }
               

   }
    return NULL;
}





Добавлено @ 13:31
Цитата
Wizard, 15.3.2005,  12:49]Написать паттерн можно просто механически, зная, как он реализуется, не нужно думать над тем, как спроектировать классы, и пр.
Ведь не зря же эти паттерны называются ПАТТЕРНАМИ ПРОЕКТИРОВАНИЯ, а не ПРОГРАММИРОВАНИЯ!


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


--------------------
С уважением, Вячеслав Ермолаев
PM MAIL WWW ICQ   Вверх
Vyacheslav
Дата 15.3.2005, 13:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 2124
Регистрация: 25.3.2002
Где: Москва

Репутация: 2
Всего: 59



Кстати, если заменим enum на String, а при создании объекта Fabric будем передоватьимя сборки, то получаем фабрику классов, совершеено независимую от классов, которые она будет создавать
Но, естесвенно, это всего лишь первый набросок, поэтому и возник вопрос. Может кто уже давно сделал лучше.


--------------------
С уважением, Вячеслав Ермолаев
PM MAIL WWW ICQ   Вверх
Domestic Cat
Дата 15.3.2005, 19:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5452
Регистрация: 3.5.2004
Где: Dallas, US

Репутация: 9
Всего: 172



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

Гоф цитировать мне не надо, а лучше взглянуть на класс диаграмму для этого паттерна. И узнать, что все-таки у них каждая конкретная фабрика - сабкласс AbstractFactory знает о тех объектах, которые она должна произвести.
Цитата
●    ConcreteFactory (MotifWidgetFactory, PMWidgetFactory) implements the operations to create concrete product objects.


Цитата
В С++ Virtual Constuctor либо имитируется с помощью switch или if, что довольно примитивно и требует при добавлении нового класса вносить изменение в класс Фабрики, либо с помощью
map-таблицы сожержащий некий ключ(например текстовое имя ) и указатель на функцию, создающую объект класса, соответсвующего ключу.


Интересно, и где это в абстрактной фабрике свичи, ифы или мапы? Хотя бы на пример в Гамме посмотри...

Цитата
Кстати, если заменим enum на String, а при создании объекта Fabric будем передоватьимя сборки, то получаем фабрику классов, совершеено независимую от классов, которые она будет создавать
Но, естесвенно, это всего лишь первый набросок, поэтому и возник вопрос. Может кто уже давно сделал лучше

Ничего особо хорошего в этом методе нет, поскольку рефлекшн - не самый быстрый способ создать класс.
Никто конечно не мешает таким образом реализовать паттерн. Только вот он никоим образом не будет подходить всем и вся. Потому, что основная мотивация абстрактной фабрики (смотреть опять-таки ГоФ)
Цитата
●    a system should be independent of how its products are created, composed, and represented.
●    a system should be configured with one of multiple families of products. 
●    a family of related product objects is designed to be used together, and you need to enforce this  constraint. 
●    you want to provide a class library of products, and you want to reveal just their interfaces, not  their implementations. 

Заметь - нигде не сказано, что фабрика не должна о чем-то знать. СИСТЕМА не должна знать. Главная цель - иметь возможность легко создавать группы взаимозаменяемых объектов, причем с минимальным вмешательством в систему. И способы, описанные в Дизайн Паттернах хороши и тем, что они служат поставленной задаче, и своей простотой.

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

Цитата
Ну спасибо за азы. Бестолковые, между прочим. Ну уж никак не думал, что "релизовать паттерн" воспримется как "написать паттерн".

Если тебя не поняли - это отчасти и твоя вина. В английском техническом языке "to realize" означает "make real or concrete; give reality or substance to; "our ideas must be substantiated into actions"".
Весьма странные слова для модератора.


--------------------

PM   Вверх
[Last]Wizard
Дата 15.3.2005, 19:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 113
Регистрация: 20.7.2004
Где: Минск, Беларусь

Репутация: 4
Всего: 10



Vyacheslav, если тебе нужна фабрика, которая
Цитата(Vyacheslav @ 15.3.2005, 12:27)
соверршено становится независима от классов, объекты которых она создает

то посмотри Activator.CreateInstance().

Хотя до паттерна "Абстрактная фабрика" это не дотягивает.

PM ICQ   Вверх
Vyacheslav
Дата 15.3.2005, 20:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 2124
Регистрация: 25.3.2002
Где: Москва

Репутация: 2
Всего: 59



Цитата(Domestic @ 15.3.2005, 19:22)
Интересно, и где это в абстрактной фабрике свичи, ифы или мапы? Хотя бы на пример в Гамме посмотри...

То что одним из возможных вариантов реализации Абстрактной Фабрики явлется реализация посредством Фабричного Метода - это, надеюсь возражений не вызывает? Если вызывает- отсылаю к Гамму. Смотрим Фабричный метод. Там используется класс Creator, который может содержать параметризованный фабричный метод
Так если Вы заглянете в книжку, то увидидите то, что в реализации для этого метода на С++ используется конструкция if.
Код

Product* Creator::Create(ProductID id)
{
if (id == MINE) return new MyProduct;
if (id == YOURS) return new YourProduct;
// выполнить для всех остальных продуктов ... например еще штук для 20 :) 

return 0;
}

smile Это к вопросу о советах насчет куда глядеть
Это во первых. А во вторых, надо понимать, что там даны всего лишь примеры для иллюстрации того, как реализация паттерна будет выглядеть на конкретном языке и для определенного примера. К тому же качество кода в примерах весьма посредственное, но другого там и не требовалось. Ибо цель примеров - донести идею, а не научить программировать. И естественно для С++ это можно реализовать более удачно например так
Цитата(Domestic @ 15.3.2005, 19:22)
Если тебя не поняли - это отчасти и твоя вина. В английском техническом языке "to realize" означает "make real or concrete; give reality or substance to; "our ideas must be substantiated into actions"".
Весьма странные слова для модератора.


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










--------------------
С уважением, Вячеслав Ермолаев
PM MAIL WWW ICQ   Вверх
Domestic Cat
Дата 15.3.2005, 20:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5452
Регистрация: 3.5.2004
Где: Dallas, US

Репутация: 9
Всего: 172



Цитата
То что одним из возможных вариантов реализации Абстрактной Фабрики явлется реализация посредством Фабричного Метода - это, надеюсь возражений не вызывает? Если вызывает- отсылаю к Гамму. Смотрим Фабричный метод. Там используется класс Creator, который может содержать параметризованный фабричный метод


Вызывает. это другой паттерн, никак не абстрактная фабрика.

Цитата
а используя уже имеющиеся описания, "реализовать"(извините другого слова подобрать не смог) его применительно к решению конкретной задачи и конкретной языковой среды?


это зависит от задачи, а о конкретной задаче пока разговора не было. Если действительнo в runtime неизвестно, объекты каких классов надo создать, тo да, такой вариант имеет место быть. Хотя oбъект можнo создавать не по аттрибуту, а по типу или стринговому названию класса - как подсказал [Last]Wizard.
http://msdn.microsoft.com/library/default....stancetopic.asp
Так прощe.


--------------------

PM   Вверх
Vyacheslav
Дата 15.3.2005, 20:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 2124
Регистрация: 25.3.2002
Где: Москва

Репутация: 2
Всего: 59



Цитата
Vyacheslav, если тебе нужна фабрика, которая
Цитата(Vyacheslav @ 15.3.2005, 12:27)
соверршено становится независима от классов, объекты которых она создает

то посмотри Activator.CreateInstance().

Хотя до паттерна "Абстрактная фабрика" это не дотягивает.

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


--------------------
С уважением, Вячеслав Ермолаев
PM MAIL WWW ICQ   Вверх
Vyacheslav
Дата 15.3.2005, 20:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 2124
Регистрация: 25.3.2002
Где: Москва

Репутация: 2
Всего: 59



[/code]
Цитата(Domestic @ 15.3.2005, 20:17)
Хотя oбъект можнo создавать не по аттрибуту, а по типу или стринговому названию класса - как подсказал [Last]Wizard.


Дедо в том, что завязка непосредственно на тип просто перемещает if'ы (или что то вместо них) в другое место. Мне приходит откуда-то некая информация , по которой я должен созлать экземпляр класса. Соответсвенно мне все равно придется где то иметь код, который доджен сопоставить значение этой информации с типом. Естественно хочется, что бы при добавлении нового класса, не требоавалось лезть туда и снова что то добавлять. В идеале , нужно сделать так, что бы вообще не требовалась перекомпиляция основной приложения. Достаточно было бы добавить класса в библиотеку (в дополнительную сборку).
Во всяком случае такой код уже работает
Требуемый экземпляр класса создается на основании значения свойства MsgType класса CMessage
Код


IExecutor* CExecutorHelper::ExecutorCreate(CMessage* Message, CConnection* Connection)
{
    Type* type = FindType(Message);
    Object* params[] = {Message, Connection };
    return __try_cast<IExecutor*>(type?Activator::CreateInstance(type,params ):NULL);
}

System::Type* CExecutorHelper::FindType(CMessage* Message )
{
    Type* types[] = Assembly::GetExecutingAssembly()->GetTypes();
    for(int i = 0; i < types->Count; i++) {
        Type* type = types[i];
        if (!type->IsAbstract && type->IsSubclassOf(__typeof(IExecutor))){
            Object* attribs[] =  type->GetCustomAttributes(__typeof(ExecutorType), false);
            if (attribs->Count && 
                __try_cast<ExecutorType*>(attribs[0])->MsgType == Message->MsgType) {
                    return type;
            }
        }
    }        
    return NULL;
}



При необходимости добавить еще одни класс(CTestExecutor)например достаточно
добавить
Код

[ExecutorType(MessageType::Test)]
__gc class CTestExecutor : public IExecutor
{
public:    
    CTestExecutor(CMessage* Message, CConnection* Connection)
        :IExecutor(Message, Connection) {}
    void Execute(void); 
};



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

void CConnection::AsyncExecutor(Object* State)
{
    Interlocked::Exchange(&m_active,1);
    while(m_active) {
        while (CMessage* Message = PeekIncoming()) {
                                                // для данного сообщения создаем соответствующий executor
            IExecutor* executor = CExecutorHelper::ExecutorCreate(Message, this);
            if (executor) {
                executor->Execute();
            }
        }
        m_threadState->WaitOne();
    }
    




--------------------
С уважением, Вячеслав Ермолаев
PM MAIL WWW ICQ   Вверх
Domestic Cat
Дата 15.3.2005, 20:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5452
Регистрация: 3.5.2004
Где: Dallas, US

Репутация: 9
Всего: 172



Ну в такой ситуации - я согласен. Если б сначала написал о задаче, и разговора б не было smile


--------------------

PM   Вверх
Vyacheslav
Дата 16.3.2005, 10:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 2124
Регистрация: 25.3.2002
Где: Москва

Репутация: 2
Всего: 59



Возможно. То что я реализовал - это первое, что пришло мне в голову при поверхностном обзоре возможностей .NET. А я в нем только с начала марта. А я по опыту знаю, что первое пришедшее решение далеко не всегда эффективно. С другой стороны иммитировать виртуальный конструктор средствами pure C++ при наличии такового( CreateInstance) в среде выглядит довольно глупо. Тем более, IMHO то что написанное на чистом С++ и STL выглядит изящнее, чем его эквивалент на managed C++. Отсюда и возник первоначальный вопрос
Добавлено @ 10:44
Вот чертов managed C++
Чтобы в pure С++ закешировать список типов мне достаточно было указать static
Вместо
Код

System::Type* CExecutorHelper::FindType(CMessage* Message )
{
    Type* types[] = Assembly::GetExecutingAssembly()->GetTypes();



использовать
Код

System::Type* CExecutorHelper::FindType(CMessage* Message )
{
    static Type* types[] = Assembly::GetExecutingAssembly()->GetTypes();


но в managed C++ это не проходит. Ругается компилятор. Теперь придется что то изобретать дополнительно.


--------------------
С уважением, Вячеслав Ермолаев
PM MAIL WWW ICQ   Вверх
Vyacheslav
Дата 16.3.2005, 11:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 2124
Регистрация: 25.3.2002
Где: Москва

Репутация: 2
Всего: 59



Во как получилось. В полном противоречии со стандартом С++ smile
Код

__gc class CExecutorHelper
{
public:
    static IExecutor* ExecutorCreate(CMessage*, CConnection*);
private:
    static System::Type* FindType(CMessage*);
    static System::Type* m_types[] = System::Reflection::Assembly::GetExecutingAssembly()->GetTypes();
};



--------------------
С уважением, Вячеслав Ермолаев
PM MAIL WWW ICQ   Вверх
arilou
Дата 16.3.2005, 20:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Великий МунаБудвин
****


Профиль
Группа: Экс. модератор
Сообщений: 2646
Регистрация: 15.7.2004
Где: город-герой Минск

Репутация: 21
Всего: 61



Честно говоря, просмотрев эту ветку, у меня возник немного другой вариант. Напишу на C#, т.к. мне это ближе. Для моей реализации необходимо 2 допущения:

1) Каждому классу, наследующемуся из MyApp.MyNamespace.MyMessage соответствует класс MyMessageDescendantExecutor, где MyMessageDescendant - это имя класса-потомка MyMessage. Класс MyMessageDescendantExecutor должен находиться в том же namespace, что и MyMessageDescendant.

2) Все Executor'ы соответствуют интерфейсу IExecutor

Код

public interface IExecutor
{
  void Populate(MyMessage message);
  void Execute();
}

public abstract class MyMessage
{
  // ...... реализация не имеет значения
}

public class TestMessage : MyMessage
{
  // ...
}

public class TestMessageExecutor : IExecutor
{
  // ...
}

class Connection 
{
  private const string executorToken = "Executor";
  private static Hashtable _typeMap = new Hashtable();

  private static IExecutor lookupExecutor(MyMessage msg)
  {
     Type execKey = msg.GetType();
     string execName = execKey.FullName + executorToken;
     
     
     if(_typeMap.ContainsKey(execKey))
       return Activator.CreateInstance(_typeMap[execKey] as Type) as IExecutor;
   
    // для простоты считаем, что все в одном assembly
    Type execType = Assembly.GetExecutingAssembly().GetType(execName);
    _typeMap.Add(execKey, execType);

    // можно кэшировать не типы, а сами инстансы, если задача того требует
    return Activator.CreateInstance(_typeMap[execKey] as Type) as IExecutor;
  }

  void ProcessMessages()
  {
    // проверки на null убраны в целях экономии :)
    while (MyMessage message = PeekIncoming()) 
    {
       IExecutor executor = lookupExecutor(message);
       executor->Populate(message);
       executor->Execute();
     }
   }
}


В результате велосипед не изобретен, пользуемся стандартными средствами. Как написано в комментарии, можно кэшировать не типы executor'ов, а инстансы.

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

P.S. 1) Не заметил, писал ли кто-нить, что Activator.CreateInstance в сочетании с информацией о типах реализует абстракную фабрику классов, т.е. такую, которая умеет создавать инстансы любых типов.
2) Добавление других сообщений и executor'ов происходит с минимальными издержками - добавляешь 2 класса, и вуаля - все работает smile

Это сообщение отредактировал(а) arilou - 16.3.2005, 20:35


--------------------
user posted imageuser posted image
PM WWW ICQ   Вверх
Vyacheslav
Дата 17.3.2005, 10:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 2124
Регистрация: 25.3.2002
Где: Москва

Репутация: 2
Всего: 59



Тут оказалась скрыта другая часть задачи. Дело в том, что при таком подходе проблема просто перетечет в PeekIncoming(). От клиента приходит массив байтов, с помощью которого заполняется CMessage. Если использовать Ваш подход, то мне все равно на основании данных, содержащихся в массиве необходимо создать соответсвующий этим данным класс XXXMessage, а затем используя имя класса создать экземпляр класса XXXMessageExecutor. При этом все равно вопрос остается открытым : нужно приходящим данным сопоставить соответствущий класс( теперь не Executor, а Мessage) и создать его экземпляр.
Здесь есть один неприятный момент: клиентская часть уже исторически унаследована, может работать на различных платформах и переделать ее таким образом, что бы она вместо линейного массива данных передавала непосредственно объект, нельзя. Интерфейс и протокол обмена определен и зафиксирован и моя программа дожна обрабатывать сообщения, поступающие от него. Проблема в том, что их в общем случае очень много, а я должен обрабатывать ограниченную часть, но никто не гарантирует, что после некоторого времени, потребуется зайдействовать дополнительные команды из этого набора. Также не исключается возможность, что добавлять обработки новых сообщения буду не я и даже не моя фирма. Поэтому и хочется по максимому облегчить процесс добавления
В моем случае при этом добавляется один класс и, я пока не силен в .NET, мне кажется при внесении небольших изменений, можно дело поставить таким образом, что добавление новых классов будет производится просто посредством подмены некоторой загружаемой библиотеки (сборки?)


--------------------
С уважением, Вячеслав Ермолаев
PM MAIL WWW ICQ   Вверх
arilou
Дата 17.3.2005, 13:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Великий МунаБудвин
****


Профиль
Группа: Экс. модератор
Сообщений: 2646
Регистрация: 15.7.2004
Где: город-герой Минск

Репутация: 21
Всего: 61



Цитата(Vyacheslav @ 17.3.2005, 10:29)
Если использовать Ваш подход, то мне все равно на основании данных, содержащихся в массиве необходимо создать соответсвующий этим данным класс XXXMessage, а затем используя имя класса создать экземпляр класса XXXMessageExecutor. При этом все равно вопрос остается открытым : нужно приходящим данным сопоставить соответствущий класс( теперь не Executor, а Мessage) и создать его экземпляр.


Наверняка массив байт содержит в начале некий заголовок, который указывает на тип сообщения. Я прав? Если так, то можно сделать следующее (в целях упрощения заголовок будет типа byte, т.е. имеем 255 различных типов сообщений):

Код

using System;
using System.Reflection;
using System.Collections;

namespace ConsoleApplication1
{
    public class Message
    {
        // ...
    }

    // атрибут, при помощи которого будем отпределять, какому заголовку
    // соответствует определенный класс сообщения
    public class MessageTypeAttribute : System.Attribute
    {
        private byte _id = 0;

        public MessageTypeAttribute(byte id) : base()
        {
            _id = id;
        }

        public byte ID { get { return _id; } }
    }


    // добавляем метаданные, описывающие данный класс сообщения
    // в данном случае это атрибут, связывающий класс сообщения
    // с определенным заголовком
    // комилятор C# достаточно умён, чтобы определить, что на самом деле
    // имеется ввиду MessageTypeAttribute, а не просто MessageType :)
    [MessageType(1)]
    public class TestMessage : Message
    {
        // ...
    }

    // данный класс является хранилищем всех типов сообщений,
    // сопоставленных с соотв. заголовками
    public class MessageRepository
    {
        // здесь храним типы сообщений
        private static Hashtable _msgMap = new Hashtable();

        // возвращаем тип сообщения по заголовку
        public static Type GetMessageType(byte msgID)
        {
            if(_msgMap.ContainsKey(msgID))
                return _msgMap[msgID] as Type;
            else
                throw new ApplicationException(String.Format("Message type for {0} is not registered", msgID));
        }

        public static void Init()
        {
            // из текущей сборки считаем все типы, которые наследуются
            // из Message
            Type[] exportedTypes = Assembly.GetExecutingAssembly().GetExportedTypes();

            foreach(Type typ in exportedTypes)
            {
                if(typ.IsSubclassOf(typeof(Message)))
                {
                    // если наш тип - наследник Message, считываем атрибут MessageType
                    // и записываем данный тип в Hashtable. Ключ - значение свойства
                    // ID, указанное в атрибуте
                    object[] attrs = typ.GetCustomAttributes(typeof(MessageTypeAttribute), false);

                    // мы ожидаем что атрибует будет, и что он будет один
                    // не будем заморачиваться :)
                    _msgMap.Add((attrs[0] as MessageTypeAttribute).ID, typ);
                }
            }
        }
    }

    /// <summary>
    /// Summary description for Class1.
    /// </summary>
    class App
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            MessageRepository.Init();

            // выводим найденный тип
            Console.WriteLine(MessageRepository.GetMessageType(1).ToString());
            Console.ReadLine();
        }
    }
}


Цитата(Vyacheslav @ 17.3.2005, 10:29)
В моем случае при этом добавляется один класс и, я пока не силен в .NET, мне кажется при внесении небольших изменений, можно дело поставить таким образом, что добавление новых классов будет производится просто посредством подмены некоторой загружаемой библиотеки (сборки?)


Мой приведенный выше пример можно расширять следующим образом:
1) Можно редактировать исходный код - добавлять новые сообщения и executor'ы - это при условии, что есть доступ к исходникам

2) Выносим Message и IExecutor в отдельную общую сборку. В MessageRepository.Init заружаем все сборки из определенного каталога (например, components), перебираем все загруженные сборки, выполняем ту же процедуру (см. метод Init) для каждого типа, удовлетворяющего условию.

Разработка дополнительных типов сообщений потребует от других фирм поставить референс на Вашу общую сборку (там, где определен класс Message и интерфейс IExecutor), реализовать их и поместить в каталог components, откуда ваша программа благополучно считает их в repository.

Наедюсь, что это Вам поможет. smile

P.S. Кстати, таким же образом можно реализовать и загрузку и сопоставление всех Executor'ов.

P.P.S Таким образом, функция PeekIncoming может выглядеть следующим образом:
Код

Message PeekIncoming()
{
    // псевдокод
    byte id = getMessageHeader().ID;

    Message msg = Activator.CreateInstance(MessageRepository.GetMessageType(id)) as Message;
    msg.ReadFromStream(getMessageStream());

    return msg;
}


Это сообщение отредактировал(а) arilou - 17.3.2005, 13:05


--------------------
user posted imageuser posted image
PM WWW ICQ   Вверх
Vyacheslav
Дата 17.3.2005, 14:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 2124
Регистрация: 25.3.2002
Где: Москва

Репутация: 2
Всего: 59



Насчет типа сообщения Вы правы. Только сначала идет длина, потом тип.
Насчет же всего прочего, я примерно так и делал за некоторым исключением.
Во первых я таким образом создавал не Message, а Executor

Код

__gc class MessageType : public System::Attribute
{
public:
    MessageType(Byte MsgType)
        :m_value(MsgType){}
    __property  get_Value() { return m_value; }
private:
    Byte m_value; 
};

//..

[MessageType(MSG_TEST)]
__gc class CTestExecutor : public IExecutor
{
public:    
    CTestExecutor(CMessage* Message, CConnection* Connection)
        :IExecutor(Message, Connection) {}
    void Execute(void); 
};


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


А теперь вопрос. Можно ли заменить в принципе это десериализацией при условии при фиксированном и неизменном формате присылаемых данных?
Ведь приведенный вами код в принципе этим и является
Код

    byte id = getMessageHeader().ID;
    Message msg = Activator.CreateInstance(MessageRepository.GetMessageType(id)) as Message;
    msg.ReadFromStream(getMessageStream());



--------------------
С уважением, Вячеслав Ермолаев
PM MAIL WWW ICQ   Вверх
arilou
Дата 17.3.2005, 14:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Великий МунаБудвин
****


Профиль
Группа: Экс. модератор
Сообщений: 2646
Регистрация: 15.7.2004
Где: город-герой Минск

Репутация: 21
Всего: 61



В принципе
Цитата(Vyacheslav @ 17.3.2005, 14:08)
Можно ли заменить в принципе это десериализацией при условии при фиксированном и неизменном формате присылаемых данных?


Не могу придумать, чем тут поможет десериализция в том понятии, в котором она используется в .NET'e, ведь приходит некий поток байтов, который же не является бинарным отображением объекта? Если бы это были какие-нить символьные данные, то можно было бы через XSL превращать их в XML-сериализованное представление объекта, и десериализовать сам объект. На выходе получался бы экземпляр Message.

Скорее, я бы определил этот метод (ReadFromStream) в базовом классе, а в потомках бы из байтового массива на входе доставал бы данные и записывал в поля и свойства. Не зная специфики принимаемых данных ничего более умного предложить не могу smile


--------------------
user posted imageuser posted image
PM WWW ICQ   Вверх
Vyacheslav
Дата 17.3.2005, 14:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 2124
Регистрация: 25.3.2002
Где: Москва

Репутация: 2
Всего: 59



Я не стал использовать метод. Просто в конструктор тупо передаю массив и от хранится как поле класса. Для общих свойств(тип, длина итд) определил свойства, которые читают-записывают прямо из массива.


--------------------
С уважением, Вячеслав Ермолаев
PM MAIL WWW ICQ   Вверх
arilou
Дата 17.3.2005, 14:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Великий МунаБудвин
****


Профиль
Группа: Экс. модератор
Сообщений: 2646
Регистрация: 15.7.2004
Где: город-герой Минск

Репутация: 21
Всего: 61



Цитата(Vyacheslav @ 17.3.2005, 14:53)
Для общих свойств(тип, длина итд) определил свойства, которые читают-записывают прямо из массива.


Логично smile Кстати, через Activator.CreateInstance можно тоже вызывать параметризованные конструкторы.


--------------------
user posted imageuser posted image
PM WWW ICQ   Вверх
Vyacheslav
Дата 17.3.2005, 16:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 2124
Регистрация: 25.3.2002
Где: Москва

Репутация: 2
Всего: 59



Я это как раз и делаю smile. И уже внедрил Hashtable
Код

IExecutor* CExecutorHelper::ExecutorCreate(CMessage* Message, CConnection* Connection)
{
    Type* type = __try_cast<Type*>(m_typesMap->Item[__box(Message->MsgType)]);
    Object* params[] = {Message, Connection };
    return __try_cast<IExecutor*>(type?Activator::CreateInstance(type,params ):NULL);
}


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


--------------------
С уважением, Вячеслав Ермолаев
PM MAIL WWW ICQ   Вверх
arilou
Дата 17.3.2005, 16:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Великий МунаБудвин
****


Профиль
Группа: Экс. модератор
Сообщений: 2646
Регистрация: 15.7.2004
Где: город-герой Минск

Репутация: 21
Всего: 61



Ну тогда топик действительно можно закрывать smile


--------------------
user posted imageuser posted image
PM WWW ICQ   Вверх
Страницы: (2) [Все] 1 2 
Ответ в темуСоздание новой темы Создание опроса
Прежде чем создать тему, посмотрите сюда:
mr.DUDA
THandle

Используйте теги [code=csharp][/code] для подсветки кода. Используйтe чекбокс "транслит" если у Вас нет русских шрифтов.
Что делать если Вам помогли, но отблагодарить помощника плюсом в репутацию Вы не можете(не хватает сообщений)? Пишите сюда, или отправляйте репорт. Поставим :)
Так же не забывайте отмечать свой вопрос решенным, если он таковым является :)


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, mr.DUDA, THandle.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Общие вопросы по .NET и C# | Следующая тема »


 




[ Время генерации скрипта: 0.1318 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.