我正在努力学习MVP,但有些事情让我望而却步;如果Presenter将视图用作界面,那么View不能只是一个简单的控件渲染。想象一下,尝试编写一个打字练习游戏,其中单词随机生成到用户界面中,用户必须在屏幕下方输入单词。
因此视图将具有以下方法:
public interface View {
addWord(String word, double x, double y); // or possibly (Word word)
moveWord(String word, double distance);
removeWord(String word);
setScore(int score);
registerKeyListener(KeyListener listener);
// other stuff
}
但最终VIEW必须负责创建自定义控件。这里省略了很多代码,但希望这足以说明我的意思。例如:
public class SwingView {
private JPanel thePanel;
private Map<String, WordComponent> currentWords = new HashMap<>();
public SwingView() {
thePanel = new JPanel(new WordLayout());
// other stuff
}
public void addWord(String word, double x, double y) {
WordComponent newWord = new WordComponent(word);
currentWords.put(word, newWord);
Point2D.Double point = new Point2D.Double(x, y);
thePanel.add(newWord, point);
}
public void removeWord(String word) {
WordComponent theWord = currentWords.get(theWord);
thePanel.remove(theWord);
}
}
View实现已经有了逻辑。它维持Map
的{{1}}。我在这里有两个类,WordComponent
和WordLayout implements LayoutManager2
(或其他东西,但这甚至是更多的代码)。
从理论上讲,演示者应该对Swing一无所知,所以我可以使用可能会记录到控制台或其他东西的模拟器进行单元测试。但是简单地管理Swing对象本身就是一项工作。或者,如果我想将此应用程序转换为Tomcat网页,该怎么办?现在,类WordComponent extends JLabel
正在管理移动单词的AJAX调用。它依赖于AJAX框架,这更多的工作卸载到ServletView
。
总结:View
实现是否应该具有管理自己组件的“逻辑”?
跟进:我上面写的代码可能甚至不会响应,因为View
和Model
不能处理事件调度线程(或者,它们可能是更差)。交出显示事件调度线程更新的代码在哪里?或者,Presenter
是否应该在Event Dispatch线程上?
编辑:我刚想到了一个想法。拥有特定于平台的子演示者 了解实现细节,例如您是否正在使用Swing或其他内容。
Edit2:还有一个问题,基于@DuncanJones的答案。想象一下,我想把逻辑放在一起,让游戏可以调整大小,并根据新的大小调整一切的大小。这个逻辑会出现在Presenter
还是View
?
答案 0 :(得分:4)
View组件必须包含足够的逻辑来显示用户的界面。根据所使用的框架,View中可能会有相当多的代码。重要的是确保业务逻辑位于Presenter中。
关于您的辅助查询,当View调用它们时(在Swing的情况下),将在EDT上调用所有Presenter方法。除非Presenter所要求的操作微不足道,否则我会立即启动后台线程来完成工作。该线程将在使用SwingUtilities.invokeLater()
完成后更新视图。
事实上,为了避免与Swing绑定,我倾向于将自己的EventDispatcher
类传递给每个Presenter。这是一个与SwingUtilities
方法相同的界面。如有必要,我可以在另一个班级替换。
旁注:这可以使用JUnit对Presenter进行单元测试很困难,因为Presenter方法(和单元测试)将在后台线程之前完成。我倾向于构造每个Presenter,其中Executor
负责运行后台线程。然后,在单元测试环境中,我传入一个特殊的Executor
实现,该实现立即在同一个线程上执行run()
方法。这可确保单元测试是单线程的。例如:
public class SingleThreadExecutor implements Executor {
@Override
public void execute(Runnable command) {
command.run();
}
}
答案 1 :(得分:0)
MVP模式的不同风格点是将业务逻辑与视图逻辑分离。考虑到这一点,拥有一个包含大量显示逻辑的视图是完全没问题的。但是有一些警告:
不要将您的小部件用作数据存储。小部件的目的是呈现内容或接收输入。演示者和模型负责保持状态。如果视图是有状态的,那么演示者应该知道并协调它。例如,窗口大小是大多数时候视图的关注点。但是如果要将其保留到某个用户设置文件中,则必须参与演示者。
不要求视图是单个的整体类。只需要有一个实现演示者期望的接口的对象,以及生成事件的人。例如,Swing使用TableModel之类的组件,其中可以放入用于指定数据表示的代码。此外,JavaFX和Android可以减少样板代码以设置视图。
当然,必须在正确的线程中处理事件。您可以使用事件总线,可以告诉您做正确的事情,或者将ExecutorService传递到每个视图中。此外,演示者要调用的每个方法都必须将工作卸载到框架的首选线程。最好将这种担忧从演示者中解脱出来,否则将很难对其进行测试。正确的事情取决于框架。