Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Дизайн класса http запроса


Автор: SABROG 25.5.2010, 11:58
Хочу сделать разную реакцию программы на получение ответа от сервера на разные типы запросов. Очередь запросов представлена подобным образом:

// псевдокод
Код

std::vector<Request> queue;


Код

struct Request
{
    enum Type {HtmlPage, File};
    std::string url;
    IODevice* device;
    Type type;
};


Ответ от сервера приходит в один метод типа:

Код

void MyClass::requestFinished(IODevice* device)
{
    Request request = getRequestByDevice(device); // метод проходит по очереди и выцепляет нужный запрос
    if (request == Request::HtmlPage) {
        doHtmlPage();
    }
    else if (request == Request::File) {
        doFile();
    }
}


Думал сделать както-то так:

Код

class AbstractRequest;

std::vector<AbstractRequest> queue;

class AbstractRequest
{
public:
    virtual void do() = 0;
    std::string url;
    IODevice* device;
};

class RequestHtmlPage : AbstractRequest
{
public:
    void do()
    {
        doHtmlPage();
    }
};

class RequestFile : AbstractRequest
{
public:
    void do()
    {
        doFile();
    }
};

void MyClass::requestFinished(IODevice* device)
{
    AbstractRequest request = getRequestByDevice(device); // метод проходит по очереди и выцепляет нужный запрос
    request.do();
}


Но я бы хотел иметь возможность точно определять какого типа объект класса в рантайме. Как я понимаю это единственный вариант при использовании примера выше без использования RTTI:

Код

    AbstractRequest arequest = getRequestByDevice(device);
    RequestHtmlPage* htmlrequest = dynamic_cast<RequestHtmlPage*>(&arequest);
    RequestFile* filerequest = dynamic_cast<RequestFile*>(&arequest);

    if (htmlrequest)
        doStuffForHtmlPage();
    else if (filerequest) {
        doStuffForFile();
    }


У кого есть мысли как это лучше организовать?

Автор: djamshud 25.5.2010, 12:13
Для dynamic_cast используется RTTI. В вашем случае для doStuff..() он не нужен, проще и правильнее организовать специфичные действия прямо в виртуальном do(). Лично я использую проверку на тип только в таких случаях:

Код
class iface{
 virtual foo(iface*);
};

class bar:public iface{
 foo(iface *obj){
  if(dynamic_cast<bar*>(obj)){
   ...
  }
  ...
 }
};


То есть проверяю принадлежность объекта к текущему наследованному типу.

Автор: jonie 25.5.2010, 12:22
зачем очередь представлена как вектор?... впрочем, не так и важно.
без RTTI - можно ввести поле enum в AbstractRequest и по нему ориентироваться, т.е. без dynamic_cast<>.
Нечто вроде:
1) класс RequestFile, RequestHtml наследники от AbstractRequest , получают данные в методе do().
2) классы обработчики реквестов, их экземпляры подтягиваются в конце do() (ну или у вас где dinamic_cast) через фабрику.. вот тут можно разрулить - подтягивать ли фабрикой новые экземпляры обработчиков, или выдавать singleton-ы.... Фабрика может судить о том что ей создать исходя из поля в AbstractRequest 

Сумбурно как-то.. но наверно можно развить идею

Автор: SABROG 25.5.2010, 12:29
Вот оно как...

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

Автор: bsa 25.5.2010, 16:11
SABROG, тебе не кажется, что этот вопрос в данном разделе не очень уместен?  smile 

Автор: mes 25.5.2010, 17:45
SABROG, имхо разбивать на классы лучше не по запросам, а по обработчикам.. 

Автор: SABROG 25.5.2010, 18:09
Цитата(bsa @  25.5.2010,  16:11 Найти цитируемый пост)
SABROG, тебе не кажется, что этот вопрос в данном разделе не очень уместен?

Вопрос как бы по дизайну, я не знаю как подобное решают профессиональные программисты.  smile 

Цитата(mes @  25.5.2010,  17:45 Найти цитируемый пост)
SABROG, имхо разбивать на классы лучше не по запросам, а по обработчикам.. 

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

Код

struct Request
{
    enum Type {HtmlPage, File};
    std::string url;
    IODevice* device;
    Type type;
};

class HtmlHandler {
public:
    explicit HtmlHandler(const Request& request)
    doStuff();
};

class FileHandler {
public:
    explicit FileHandler(const Request& request)
    doStuff();
};

void MyClass::requestFinished(IODevice* device)
{
    Request request = getRequestByDevice(device); // метод проходит по очереди и выцепляет нужный запрос
    if (request == Request::HtmlPage) {
        htmlHandler handler(request);
        handler.doStuff();
    }
    else if (request == Request::File) {
        fileHandler handler(request);
        handler.doStuff();
    }
}


Или ты имеешь ввиду шаблонные классы с типовой специализацией?

Автор: jonie 25.5.2010, 20:02
вместо 
Цитата

Код

    Request request = getRequestByDevice(device); // метод проходит по очереди и выцепляет нужный запрос
    if (request == Request::HtmlPage) {
        htmlHandler handler(request);
        handler.doStuff();
    }
    else if (request == Request::File) {
        fileHandler handler(request);
        handler.doStuff();
    }

я предлагал делать фабрику, и извлекать экземпляры обработчиков из неё - if-ы уйдут банальнейше...

Автор: SABROG 25.5.2010, 21:05
Цитата(jonie @  25.5.2010,  20:02 Найти цитируемый пост)
я предлагал делать фабрику, и извлекать экземпляры обработчиков из неё - if-ы уйдут банальнейше... 

Я понял, но нужно рассмотреть несколько реализаций фабрик, которая мне бы подошла, а то ведь тот же паттерн Factory завязывается на виртуальном наследовании, а у меня тут поле Type. Я где-то видел фабрику, которая генерила объекты по строковому параметру типа f->create("automobile"), вероятно мне придется сделать что-то похожее, но типа такого f->create(Request::HtmlPage), ну и request->doStuff().

Автор: jonie 25.5.2010, 21:15
Цитата

но типа такого f->create(Request::HtmlPage)
у вас в рексесте есть Type, используйте его...Моё личное мнение - если у вас есть возможность уйти от виртуальности, то уходите -- в дальнейшем упростит отладку и поиск багов
Цитата

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

Автор: SABROG 25.5.2010, 21:28
Цитата(jonie @  25.5.2010,  21:15 Найти цитируемый пост)
у вас в рексесте есть Type, используйте его

Значит я все-таки не до конца тебя понял. Ты предлагаешь оставить структуру как есть, но добавить класс, который будет "плодить" функторы разных классов в зависимости от Type'a?

Цитата(jonie @  25.5.2010,  21:15 Найти цитируемый пост)
отродясь такого не было вроде бы...

Abstract Factory

Автор: jonie 25.5.2010, 21:58
Цитата

Ты предлагаешь оставить структуру как есть, но добавить класс, который будет "плодить" функторы разных классов в зависимости от Type'a?
yes.  собственно фабрику. Пост мой прочти этот [http://forum.vingrad.ru/index.php?showtopic=301427&view=findpost&p=2158864]  - не нужны будут if-ы, в фабрике можно использовать std::map, да и вообще кастомизировать этот твой if по Type-у как угодно сложно\легко (заивист от фантазии).

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

Цитата

Abstract Factory 
замечу что "абстактная фабрика" ничуть не называется "фабрика" 8-)

Автор: SABROG 26.5.2010, 23:33
Почитал документацию к примерам фабрик http://sourceforge.net/projects/papafactory и дошел до такого текста:

Цитата

Comparing this latest implementation with our original ifthen.cpp implementation,
it is tempting to think that we may have produced an over-engineered and overcomplex
design to our original problem. This is possibly true. If all we wanted
was to have three functions, halve, square and integer and no scope for extra flexibility,
then there is a strong argument that the original design was the best option.


То есть у меня вообще 2 выходит. Как бы там ни было продолжу копать в направлении фабрик для саморазвития и возможности расширения функционала кода в перспективе.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)