MVP средствами С++
От: Ryadovoy  
Дата: 25.06.11 08:44
Оценка:
Вдохновившись вот этой
Автор(ы): Иван Бодягин
Дата: 25.07.2006
В наше время сложно найти разработчика, который не слышал бы о паттерне под названием Model-View-Controller или сокращенно MVC, что вообщем не удивительно, с задачей отделения данных от их представления сталкиваешься практически на каждом проекте. Однако, как ни странно, столь же сложно найти разработчика, который действительно четко себе представляет, что такое на самом деле паттерн MVC и как его можно реализовать в конкретной ситуации. Основная причина такой неоднозначности в том, что по историческим причинам данной аббревиатурой принято называть не один единственный паттерн, а целое семейство паттернов, призванное отделять представление от модели. Произошло это в силу разных обстоятельств. Отчасти из-за того что MVC не просто паттерн, а довольно объемное архитектурное решение, в котором каждый новый разработчик видел что-то свое и ставя во главу угла особенности своего проекта, реализовывал его по своему. Отчасти же из-за возраста данного паттерна, во времена его изобретения и сами приложения, и графические интерфейсы были существенно беднее чем в наше время, с тех пор они сильно эволюционировали и вместе с ними изменялся и сам паттерн. Данная статья посвящена также одному из паттернов входящих в это семейство, причинам его появления, особенностям применения, преимуществам и недостаткам, а так же описанию сопутствующих паттернов.
статьей, я решил у себя в проекте сделать что-то подобное.
При этом я наткнулся на определенные трудности.
На данный момент я пришел к следующему решению:
template<typename TEvents>
class CEventsNotifier
{
public:
    CEventsNotifier()
        :m_pListener(NULL)
    {
    }
    virtual ~CEventsNotifier()
    {
    }

public:
    void Advise(TEvents* pListener)
    {
        m_pListener = pListener;
    };
    void Unadvise()
    {
        m_pListener = NULL;
    }

public:
    void Notify(void (TEvents::*pFn)(void))
    {
        if(m_pListener)(m_pListener->*pFn)();
    };
    template<typename T1>
    void Notify(void (TEvents::*pFn)(T1), T1 P1)
    {
        if(m_pListener)(m_pListener->*pFn)(P1);
    };
    template<typename T1, typename T2>
    void Notify(void (TEvents::*pFn)(T1, T2), T1 P1, T2 P2)
    {
        if(m_pListener)(m_pListener->*pFn)(P1, P2);
    };
    template<typename T1, typename T2, typename T3>
    void Notify(void (TEvents::*pFn)(T1, T2, T3), T1 P1, T2 P2, T3 P3)
    {
        if(m_pListener)(m_pListener->*pFn)(P1, P2, P3);
    };
    template<typename T1, typename T2, typename T3, typename T4>
    void Notify(void (TEvents::*pFn)(T1, T2, T3, T4), T1 P1, T2 P2, T3 P3, T4 P4)
    {
        if(m_pListener)(m_pListener->*pFn)(P1, P2, P3, P4);
    };
    template<typename T1, typename T2, typename T3, typename T4, typename T5>
    void Notify(void (TEvents::*pFn)(T1, T2, T3, T4), T1 P1, T2 P2, T3 P3, T4 P4, T5 P5)
    {
        if(m_pListener)(m_pListener->*pFn)(P1, P2, P3, P4, P5);
    };

private:
    TEvents* m_pListener;
};

class IViewEvents
{
public:
    virtual void OnSetCelsius(double value) = 0;
    virtual void OnSetFarenheit(double value) = 0;
};

class IView : public CEventsNotifier<IViewEvents>
{
public:
    virtual void SetCelsius(double value) = 0;
    virtual void SetFarenheit(double value) = 0;
};

class CModel
{
public:
    CModel()
    {
        SetCelsius(0);
    }
    void SetFahrenheit(double value)
    {
        m_Fahrenheit = value;
        m_Celsius = (m_Fahrenheit - 32) * 5 / 9;
    }
    double GetFahrenheit()const
    {
        return m_Fahrenheit;
    }
    void SetCelsius(double value)
    {
        m_Celsius = value;
        m_Fahrenheit = m_Celsius * 9 / 5 + 32;
    }
    double GetCelsius()const
    {
        return m_Celsius;
    }
private:
    double m_Fahrenheit;
    double m_Celsius;
}

class CPresenter : public IViewEvents
{
public:
    CPresenter(IView* pView, CModel* pModel)
        :m_pView(pView),
        m_pModel(pModel)
    {
        m_pView->Advise(this);
    }
    ~CPresenter()
    {
        m_pView->Unadvise();
    }

    //IViewEvents
    virtual void OnSetCelsius(double value)
    {
        m_pModel->SetCelsius(value);
    }
    virtual void OnSetFarenheit(double value)
    {
        m_pModel->SetFarenheit(value);
    }

    void RefreshView()
    {
        m_pView->SetCelsius(m_pModel->GetCelsius());
        m_pView->SetFarenheit(m_pModel->GetFahrenheit());
    }

private:
    IView*          m_pView;
    CModel*         m_pModel;
};

class CMyView : public IView
{
private:
    virtual void SetCelsius(double value)
    {
    }
    virtual void SetFarenheit(double value)
    {
    }

    void SetCelsiusHandler(double value)
    {
        Notify(&IViewEvents::OnSetCelsius, value);
    }
    void SetFarenheitHandler(double value)
    {
        Notify(&IViewEvents::OnSetFarenheit, value);
    }
};


Преимущества использования базового класса CEventsNotifier:
1) Нет необходимости реализовывать методы Advise/Unadvise в реализации View, и хранить указатель на IViewEvents
2) View не имеет прямого доступа к Presenter-у, оно может только извещать о пользовательском вводе
3) Тяжело (неестественно) написать, и затем вызывать, функцию — getter в интерфейсе IViewEvents, и это поддерживает идеологию MVC.
4) Интерфейс IView четко декларирует какие события он может сообщать Presenter-у, и для этого кроме наследования от IView больше ничего не нужно.

Недостатки тоже есть конечно, их я как раз бы и хотел обсудить.
Re: MVP средствами С++
От: licedey  
Дата: 25.06.11 09:24
Оценка:
Здравствуйте, Ryadovoy, Вы писали:

R>Вдохновившись вот этой
Автор(ы): Иван Бодягин
Дата: 25.07.2006
В наше время сложно найти разработчика, который не слышал бы о паттерне под названием Model-View-Controller или сокращенно MVC, что вообщем не удивительно, с задачей отделения данных от их представления сталкиваешься практически на каждом проекте. Однако, как ни странно, столь же сложно найти разработчика, который действительно четко себе представляет, что такое на самом деле паттерн MVC и как его можно реализовать в конкретной ситуации. Основная причина такой неоднозначности в том, что по историческим причинам данной аббревиатурой принято называть не один единственный паттерн, а целое семейство паттернов, призванное отделять представление от модели. Произошло это в силу разных обстоятельств. Отчасти из-за того что MVC не просто паттерн, а довольно объемное архитектурное решение, в котором каждый новый разработчик видел что-то свое и ставя во главу угла особенности своего проекта, реализовывал его по своему. Отчасти же из-за возраста данного паттерна, во времена его изобретения и сами приложения, и графические интерфейсы были существенно беднее чем в наше время, с тех пор они сильно эволюционировали и вместе с ними изменялся и сам паттерн. Данная статья посвящена также одному из паттернов входящих в это семейство, причинам его появления, особенностям применения, преимуществам и недостаткам, а так же описанию сопутствующих паттернов.
статьей, я решил у себя в проекте сделать что-то подобное.

R>При этом я наткнулся на определенные трудности.
R>На данный момент я пришел к следующему решению:

R>Недостатки тоже есть конечно, их я как раз бы и хотел обсудить.


Добротная такая реализация. Это скорее в Архитектуру ПО под вопросом, нужно ли MVP для вычисления градусов.
Re[2]: MVP средствами С++
От: Ryadovoy  
Дата: 25.06.11 12:35
Оценка:
Здравствуйте, licedey, Вы писали:
L>Добротная такая реализация. Это скорее в Архитектуру ПО под вопросом, нужно ли MVP для вычисления градусов.
Пример про градусы был придуман не мной, он был взят со статьи
Автор(ы): Иван Бодягин
Дата: 25.07.2006
В наше время сложно найти разработчика, который не слышал бы о паттерне под названием Model-View-Controller или сокращенно MVC, что вообщем не удивительно, с задачей отделения данных от их представления сталкиваешься практически на каждом проекте. Однако, как ни странно, столь же сложно найти разработчика, который действительно четко себе представляет, что такое на самом деле паттерн MVC и как его можно реализовать в конкретной ситуации. Основная причина такой неоднозначности в том, что по историческим причинам данной аббревиатурой принято называть не один единственный паттерн, а целое семейство паттернов, призванное отделять представление от модели. Произошло это в силу разных обстоятельств. Отчасти из-за того что MVC не просто паттерн, а довольно объемное архитектурное решение, в котором каждый новый разработчик видел что-то свое и ставя во главу угла особенности своего проекта, реализовывал его по своему. Отчасти же из-за возраста данного паттерна, во времена его изобретения и сами приложения, и графические интерфейсы были существенно беднее чем в наше время, с тех пор они сильно эволюционировали и вместе с ними изменялся и сам паттерн. Данная статья посвящена также одному из паттернов входящих в это семейство, причинам его появления, особенностям применения, преимуществам и недостаткам, а так же описанию сопутствующих паттернов.

В нашем проекте интерфейсы посложнее (там у нас вырисовывается иерархия Presenter-ов).
Re: MVP средствами С++
От: nen777w  
Дата: 25.06.11 13:06
Оценка:
Часть с CEventsNotifier::Notify() можно было сделать на препроцессоре, тогда колличество параметров можно сделать конфигруабельным.
Не совсем понял кто и когда будет ответсвенный за вызов CPresenter::RefreshView() что бы вьюха обновила своё представление ?
Re[2]: MVP средствами С++
От: Ryadovoy  
Дата: 25.06.11 13:20
Оценка:
Здравствуйте, nen777w, Вы писали:

N>Часть с CEventsNotifier::Notify() можно было сделать на препроцессоре, тогда колличество параметров можно сделать конфигруабельным.

Не совсем понял, можете подробнее объяснить Ваше предложение?

N>Не совсем понял кто и когда будет ответсвенный за вызов CPresenter::RefreshView() что бы вьюха обновила своё представление ?

Ах, да, похоже я допустил опечатку, RefreshView нужно вызывать в CPresenter::OnSetCelsius() и в CPresenter::OnSetFarenheit()
Re[3]: MVP средствами С++
От: nen777w  
Дата: 25.06.11 14:06
Оценка:
N>>Часть с CEventsNotifier::Notify() можно было сделать на препроцессоре, тогда колличество параметров можно сделать конфигруабельным.
R>Не совсем понял, можете подробнее объяснить Ваше предложение?

CEventsNotifier — имеет ряд шаблонных методов Notify, сейчас их 5-ть на будущее (вдруг понадибится)
этот код можно генерировать при помощи препроцессора. Это просто пожелание, если хотите. Можно сделать на BOOST_PP
А можно вообще boost::bind + boost::function вместо этого заюзать.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.