我是一名学生,在一个个人java项目上工作(阅读:不是家庭作业),有一个大型GUI组件,并试图遵循MVC架构。我正在使用许多具有各种布局的嵌套jpanel构建一个复杂的视图。这些面板中按钮的actionlistener将在包含对模型和视图的引用的控制器类中实现。每个面板只包含对其直接子面板的引用,因此,当控制器需要更新其中一个深度嵌套面板中的内容时,它需要这样的代码:
view.getCardLayoutPanel().getReceptionPanel().getOrderContentsPanel().updateTable(tableModel);
似乎不太理想。下面是整个系统的一些伪代码。我在这里的代码中省略了简单的get方法,但是我所有的GUI组件类都包含get方法,这些方法返回对它们直接拥有的子面板的引用。
如何在遵守MVC的同时改善这种情况?
控制器:
public class Controller implements ActionListener {
private View view;
public Controller(View view, Model model){
this.view = view;
this.model = model;
}
private void updateOrderTable() {
TableModel tableModel; //= **call model method to query db and generate a TableModel**
// This looks bad
view.getCardLayoutPanel().getReceptionPanel().getOrderContentsPanel().updateTable(tableModel);
}
}
视图:public class View {
// Setup stuff
JFrame mainFrame = new JFrame();
myCardLayoutPanel = new MyCardLayoutPanel ();
// add cardLayoutPanel to mainframe
}
下一层:
public class MyCardLayoutPanel extends JPanel {
receptionPanel = new ReceptionPanel();
dataEntryPanel = new DataEntryPanel();
// etc
// Add all the panels to the layout
}
另一个中间面板,我将省略,遵循相同的模式…最后是包含表的JPanel。
public class OrderContentsPanel extends JPanel {
private JTable table;
private JScrollPane scrollpane;
public OrderContentsPanel() {
setLayout(new BorderLayout());
scrollPane = newJScrollPane(table);
add("Center", scrollPane) //
}
public void updateTable(TableModel tablemodel) {
table.setModel(tableModel);
}
}
用应用程序级事件丰富面板和模型可能会有所帮助。
例如,考虑您的应用程序有一个OrderEntryPanel
,它在每次用户输入订单时产生一个事件,而一个ArrivedOrdersPanel
消耗到达订单的事件。控制器并不关心这些面板的内部结构以及它们如何在视觉上相互排列。相反,控制器将专注于将事件从面板传输到模型,反之亦然。
OrderEntryPanel
看起来像这样:
public class OrderEntryPanel extends JPanel {
private final JTextField orderTextField = new JTextField(20);
private final JButton sendButton = new JButton("Send order");
private Consumer<String> consumer;
public OrderEntryPanel() {
add(new JLabel("Place your order here: "));
add(orderTextField);
add(sendButton);
sendButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
consumer.accept(orderTextField.getText());
orderTextField.setText("");
}
});
}
public void setOrderEntryConsumer(Consumer<String> consumer) {
this.consumer = consumer;
}
}
同样适用于ArrivedOrdersPanel
:
public class ArrivedOrdersPanel extends JPanel {
private JTable table = new JTable();
private DefaultTableModel tableModel = new DefaultTableModel(0, 2);
public ArrivedOrdersPanel() {
setLayout(new BorderLayout());
JScrollPane scrollPane = new JScrollPane(table);
add("Center", scrollPane);
table.setModel(tableModel);
}
public void orderArrived(String orderText) {
Integer orderNumber = Integer.valueOf(tableModel.getRowCount()+1);
tableModel.addRow(new Object[] {orderNumber, orderText});
}
}
View
类创建并初始化它们,并为它们提供getter方法(我将省略这个细节;它不应该对这个想法有太大的帮助。
模型(这里是OrderStore
)使用用户输入订单的事件,然后生成新到达订单的事件:
public class OrderStore {
private final List<Order> orders = new LinkedList<>();
private Consumer<Order> orderArrivedConsumer;
public void incomingOrder(String orderText) {
Order order = new Order(orderText);
orders.add(order);
orderArrivedConsumer.accept(order);
}
public void setOrderArrivedConsumer(Consumer<Order> consumer) {
this.orderArrivedConsumer = consumer;
}
}
最后,我们可以使用简单的lambdas连接模型和视图:
public static void main(String[] args) throws IOException {
View view = new View();
view.init();
OrderStore model = new OrderStore();
// In order not to dilute the essential point, the consumers do not switch execution control
// from Swing's event dispatcher thread to a background thread and vice versa
view.getOrderEntryPanel().setOrderEntryConsumer(
orderText -> model.incomingOrder(orderText));
model.setOrderArrivedConsumer(
order -> view.getArrivedOrdersPanel().orderArrived(order.getOrderText()));
}
因此,UI的复杂性与UI与模型的耦合是分离的。