【发布时间】:2014-02-26 01:23:57
【问题描述】:
在被动视图模型视图演示者模式中,谁负责显示视图?我找到了其他 MVP 版本的相关答案,但它们似乎不适用于被动视图版本。
我有一个使用 Java Swing 的具体示例。这很简单,但基本上我们有一个SwingCustomersView,它在内部构建了一个JPanel,其中包含一个表格(客户列表)和一个显示当前选定客户年龄的标签。当在表中选择客户时,演示者从模型中检索选定的客户年龄。我认为该示例是 MVP Passive View 的正确实现,但如果我错了,请纠正我。
问题是我们如何引导这些类?例如,如果我们想在 JFrame 中显示SwingCustomersView。如何做到这一点?我想像这样的事情:
void launcher() {
CustomersModel model = new CustomersModel();
SwingCustomersView view = new SwingCustomersView();
CustomersPresenter presenter = new CustomersPresenter(view, model);
}
这是初始接线,但尚未显示任何内容。我们如何实际显示视图? (1) launcher()、(2) SwingCustomersView 或 (3) CustomersPresenter 是否有责任显示视图?不幸的是,我认为这些都不是很好,正如您从我下面的想法中看到的那样。也许还有其他方法?
(1.a): 启动器
使 SwingCustomersView 扩展 JFrame 并将其内部 JPanel 添加到自身的内容窗格中。然后我们可以这样做:
void launcher() {
CustomersModel model = new CustomersModel();
SwingCustomersView view = new SwingCustomersView();
CustomersPresenter presenter = new CustomersPresenter(view, model);
view.setVisible(true); // Displays the view
}
但是,在这种情况下,我们不会将presenter 实例用于任何事情。这不是很奇怪吗?它只是用于接线,我们也可以删除变量并执行new CustomersPresenter(view, model)。
(2): SwingCustomersView
让SwingCustomersView 在构造函数中使用Container,它应该将它的内部JPanel 添加到其中:
void launcher() {
CustomersModel model = new CustomersModel();
JFrame frame = new JFrame("Some title");
SwingCustomersView view = new SwingCustomersView(frame.getContentPane());
CustomersPresenter presenter = new CustomersPresenter(view, model);
frame.pack();
frame.setVisible(true) // Displays the view
}
但是,与 (1) 相同的问题:presenter 实例什么也不做。似乎很奇怪。此外,使用 (1) 和 (2) 都可以在连接演示者之前显示视图,我想这在某些情况下可能会导致奇怪的结果。
(3):CustomersPresenter
让CustomersPresenter负责显示视图somwhow。然后我们可以这样做:
void launcher() {
CustomersModel model = new CustomersModel();
SwingCustomersView view = new SwingCustomersView();
CustomersPresenter presenter = new CustomersPresenter(view, model);
presenter.show() // Displays the view
}
这将解决构建后不使用它的问题。但是如果不更改CustomersView 接口或使CustomersPresenter 过于依赖底层GUI 实现,我看不到如何做到这一点。此外,显示视图听起来不像演示逻辑,因此似乎不属于演示者。
示例
public class CustomersModel {
private List<Customer> customers;
public CustomersModel() {
customers = new ArrayList<Customer>();
customers.add(new Customer("SomeCustomer", "31"));
customers.add(new Customer("SomeCustomer", "32"));
}
public List<Customer> getCustomers() {
return customers;
}
}
public class Customer {
public String name;
public String age;
public Customer(String name, String age) {
this.name = name;
this.age = age;
}
}
public interface CustomersView {
void addCustomerSelectionChangeListener(ItemListener listener);
void onNewActiveCustomer(String age);
void onNewCustomers(List<String> newCustomers);
}
public class SwingCustomersView implements CustomersView {
// Swing components here all put into a main JPanel
public void addCustomerSelectionChangeListener(ItemListener listener) {
// Add event listener to table
}
public void onNewActiveCustomer(String age) {
// Display age in label beneath table
}
public void onNewCustomers(List<String> newCustomers) {
// Display customers in table
}
}
public class CustomersPresenter {
private final CustomersView view;
private final CustomersModel model;
public CustomersPresenter(CustomersView view, CustomersModel model) {
this.view = view;
this.model = model;
initPresentationLogic();
populateView();
}
private void initPresentationLogic() {
view.addCustomerSelectionChangeListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
String selectedName = (String)e.getItem();
List<Customer> customers = model.getCustomers();
for (Customer c : customers)
if (c.name.equals(selectedName))
view.onNewActiveCustomer(c.age);
}
});
}
private void populateView() {
List<Customer> customers = model.getCustomers();
List<String> names = new ArrayList<String>();
for (Customer c : customers)
names.add(c.name);
// View will now populate its table, which in turn will call customerSelectionChangeListener
// so the view 'automagically' updates the selected contact age too
view.onNewCustomers(names);
}
}
【问题讨论】: