我有一个使用 x11 lib 来绘制一些用户界面的应用程序。现在我尝试用 MVP(模型-视图-演示者)模式重构它,但叠加了有关视图-演示者部分的一些细节。例如:我有一个左/右箭头键按下事件,通过该事件,我需要按视图上的元素(如浏览器中的选项卡)循环。现在这个事件只是从View流到Presenter,然后Presenter选择下一个要激活的元素并将其推回ui。但是(!)要选择下一个元素,我需要在 BEGIN 和 END 上有一个迭代器(C ++,在其他语言中可能只是查看对象列表末尾的引用/索引),它导致有一种特殊的方法来获取它在IView界面中,看起来不太清楚。在视图中执行按键,选择下一个元素,然后通知演示者当前活动元素不是更好吗?
我读过很多相关文章,并使用此模式检查了存储库。但还是看不懂:(
伪代码:
class IView {
public:
virtual Element* firstElement();
virtual Element* lastElement();
};
class Presenter {
public:
void onRightKeyPressed() {
if(!currentElement) {
currentElement = view->firstElement();
} else {
currentElement->resetActive();
++currentElement;
if(currentElement == view->lastElement()) {
currentElement = view->firstElement():
}
}
currentElement->setActive();
}
private:
IView* view;
Element* currentElement;
};
与类似的东西
class IView {
public:
// Notify the presenter about the currently active element
virtual void notifyElementActivated(Element* element) = 0;
// Handle arrow key press event and select the next element
virtual void handleRightKeyPressed() = 0;
};
class Presenter {
public:
void onRightKeyPressed() {
view->handleRightKeyPressed();
}
// Receive the notification from the view about the currently active element
void notifyElementActivated(Element* element) {
// React to the active element change
}
private:
IView* view;
};
class View : public IView {
public:
void handleRightKeyPressed() override {
if (!currentElement) {
currentElement = firstElement();
} else {
currentElement->resetActive();
++currentElement;
if (currentElement == lastElement()) {
currentElement = firstElement();
}
}
currentElement->setActive();
// Notify the presenter about the currently active element
notifyElementActivated(currentElement);
}
void notifyElementActivated(Element* element) override {
presenter->notifyElementActivated(element);
}
private:
Presenter* presenter;
Element* currentElement;
};
我期待这个案例有更明确的解决方案!
您的问题有不同的“正确”解决方案。选择哪一个取决于 Element 对象是什么、活动元素控制的 Presenter 逻辑有多少(如果有)以及该逻辑的外观。
如果“Element”只是一个哑标签页,并且 Presenter 几乎不包含依赖于活动元素的逻辑,则可以让 View 完全负责保存当前元素和前进/后退逻辑。为此,您的第二个解决方案看起来是一个好的开始。然而,虚拟方法
IView::notifyElementActivated
似乎已经过时了(如果演示者能够通知视图有关激活的信息,则您需要它,我不知道您是否真的需要它)。
此外,您的代码没有给出任何线索是否确实需要
Presenter::notifyElementActivated
,或者方法 View::getCurrentElement
是否足够。
在您的第一个解决方案中,当前元素的管理是演示者的一部分,只要视图没有自己的“活动页面/元素”管理,这就可以了,这可能会偏离演示者。您写下了您的担忧:
但是(!)要选择下一个元素,我需要在 BEGIN 和 END(c++,在其他语言中可能只是结尾处的引用/索引 查看对象的列表)它导致有一个特殊的方法 在IView界面中获取,看起来不太清楚。
那么,为什么不这样设计你的 IView 界面呢?
class IView {
public:
virtual const vector<Element> &elements();
}
我还建议将 Presenter 中的
onRightKeyPressed()
重命名为 gotoNextElement()
。您的视图应该有一个事件处理程序 onRightKeyPressed()
,它调用 presenter->gotoNextElement()
- 哪些键确切地导致向前或向后导航是演示者不应该关心的事情。