Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Пришлось бы создавать в Presenter-е дополнительные публичные слоты и сигналы, и коннектить View и Presenter в контексте вашего приложения.
Также объект приложения должен будет ЗНАТЬ, как именно должны связываться View и Presenter. Мне кажется, это было бы не так удобно.
Ну можно обойтись вообще без IView, да. Но тогда увеличится связанность, т.е. презентеру придется знать о конкретном View, а не об интерфейсе. IView для этого и применен, для уменьшения связанности. Подробней можно прочесть тут: www.rsdn.ru/article/patterns/ModelViewPresenter.xml#EGGAC, называется Inversion of Control.
class INotify
{
public: virtual void setText( QString ) = 0;
public signals: virtual void closeClicked() = 0;
};
class CNotify : public INotify
{
public: void setText( QString )
{
// Устанавливаем текст.
}
// Вызывается Qt при клике на кнопку.
private slots: void on_btn_close_clicked()
{
// Рапортуем о клике на кнопку.
emit closeClicked()
}
}
class CPresenter
{
public: CPresenter( INotify* view )
{
m_view = view;
connect( view, SIGNAL(closeClicked()), this, SLOT(onClose()) );
}
private slots: void onClose()
{
// Модель опустим, тут у нас как-бы логика.
view->setText( tr( L"меня закрыли :(" ) );
}
}
void main()
{
CNotify notify;
CPresenter presenter( notify );
}
class CNotify
{
public: CNotify()
{
connect( ui->btn_close, SIGNAL(clicked), this, SIGNAL(closeClicked()) );
}
signals: void closeClicked();
public slots: void setText( QString )
{
// Устанавливаем текст.
}
}
class CPresenter
{
signals: void setText( QString );
public slots: void onClicked()
{
emit setText( tr( L"меня закрыли :(" ) );
}
}
void main()
{
CNotify notify
CPresenter presenter
connect( notify, SIGNAL(closeClicked()), presenter, SLOT(onClicked()) );
connect( presenter, SIGNAL(setText(QString)), notify, SLOT(setText(QString)) );
}
В описанном мной случае объект, в котором конкретный View передается в Presenter, не знает, как именно происходит связь между View и Presenter-ом. В предлагаемом вами варианте при изменении интерфейса придется менять код в вашем объекте, а при использовании интерфейса организация связи View и Presenter сокрыта в Presenter.
Вообще, основная идея в том, чтобы однажды написав IView, можно было бы создавать конкретные View, при этом код основного приложения абсолютно не менялся. Например, есть фабрики классов, которые выдают конкретные View (окошки вашего приложения). Допустим фабрики ClassicViewFactory, GlamourViewFactory и WebViewFactory, которые наследуются от AbstractViewFactory. Далее, создавая на этапе запуска приложения конкретную фабрику (скажем прочитав соответствующий флаг из файла настроек приложения), имеем абсолютно разный внешний вид приложения. При этом код приложения вообще не меняется. При добавлении нового стиля приложения — добавляется новая фабрика, а в написанный код добавится лишь код:
В приведенном вами примере, функция main() знает о том, как именно происходит связь между CNotify и CPresenter. Теперь при изменении интерфейсной части CNotify придется переписывать также функцию main().
роме того, метод QObject::connect() выполняется в динамике, в отличие передачи объекта по интерфейсу. А значит вы лишаетесь статической проверки того, насколько вы всё правильно сделали
Знает, просто это не так заметно. На самом деле механизм сигналов и слотов — это реализация паттерна Observer на уровне MOC-компилятора, а значит объект подписки (Subject) знает о наблюдателях (Observer), а также знает указатели на функции уведомления у Observer.
Я писал не о том, что объект не знает об объекте View, а о том, что он не знает, как именно происходит связь между View и Presenter
Более того, с помощью MVP вы можете сделать представление не окошком, а консолью, или вообще веб-формой. Так что абстрактная фабрика действительно может пригодится в случае, если ваше приложение должно быть очень гибкое в плане UI, т.е. легко менять все View в приложении в зависимости от выбранного пользователем стиля.
Реализация Model-View-Presenter в Qt