Java MVC模式下子窗口实现相关技术疑问
嘿,咱们来逐个拆解你的三个问题,结合你现有的代码梳理清楚MVC架构里的职责划分,帮你把代码捋得更规范~
1)使用Observer-Observable模式从Model类调用View类的方法是否正确?
这个方向是对的,但要注意边界问题:Model作为Observable,只负责维护业务状态、在状态变化时通知所有Observer(也就是View),绝对不能直接调用View的方法。你现在的实现里,Model通过setChanged()+notifyObservers()发布状态变化,View的update()方法根据Model的状态自主做出响应,这完全符合Observer模式的设计思想——Model不需要知道View的具体实现,只负责传递状态;View自己决定如何响应状态变化。
小细节提醒:你的View类持有Model引用是没问题的,但要确保Model里绝对不要持有View的引用,保持单向依赖(View依赖Model,Model不依赖View),这样才能保证Model的独立性,方便后续复用或修改。
2)子窗口的创建应该委托给View类还是在Model类中执行?
毫无疑问,子窗口的创建必须是View层的职责!Model的核心职责是处理业务逻辑、维护数据状态,所有和UI相关的操作(窗口创建、组件渲染、布局调整等)都应该交给View来做。
你现在在View的update()方法中创建NewWindow的方向是对的,但可以进一步优化:把创建子窗口的逻辑封装成View的独立方法(比如createNewWindow()),而不是直接写在update()里,让代码结构更清晰。另外,建议把NewWindow也看作View层的一部分,不要让它持有Model引用(你当前的NewWindow里有Model model但没初始化,这是个小问题),如果子窗口需要响应Model状态变化,可以让它也实现Observer接口。
3)如何在Controller类中为子窗口的按钮设置监听器?
因为子窗口是运行时动态创建的,初始化Controller时确实拿不到它的实例,这里给你两种优雅的解决方案:
方案思路:让主View在创建子窗口后通知Controller
修改View类,添加Controller的引用(通过延迟设置避免循环依赖),当子窗口创建完成后,调用Controller的方法来绑定监听器;同时让NewWindow暴露按钮实例,方便Controller获取并添加监听。
优化后的代码示例
咱们基于你的代码调整一下,看看具体实现:
调整后的View类
import java.awt.*; import java.util.Observable; import java.util.Observer; import javax.swing.*; public class View implements Observer { JFrame frame; JPanel panel; JButton button; Model model; Controller controller; // 持有Controller引用,用于通知子窗口创建完成 public View(Model model) { this.model = model; initMainWindow(); } // 初始化主窗口的逻辑封装 private void initMainWindow() { frame = new JFrame(); panel = new JPanel(); button = new JButton("Press"); panel.add(button); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(500, 500); frame.add(panel); frame.setVisible(true); } // 延迟设置Controller,避免循环依赖 public void setController(Controller controller) { this.controller = controller; } @Override public void update(Observable ob, Object extra_arg) { if (model.method_var == 0) { NewWindow newWindow = createNewWindow(); // 通知Controller绑定子窗口按钮的监听器 controller.bindNewWindowButton(newWindow.getButton()); } } // 创建子窗口的逻辑封装 private NewWindow createNewWindow() { return new NewWindow(); } }
调整后的NewWindow类
import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; public class NewWindow { JFrame frame; JPanel panel; JButton button; public NewWindow() { initNewWindow(); } private void initNewWindow() { frame = new JFrame(); panel = new JPanel(); button = new JButton("Press again"); panel.add(button); frame.setSize(100, 100); frame.add(panel); frame.setVisible(true); } // 暴露按钮给Controller public JButton getButton() { return button; } }
调整后的Controller类
import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class Controller { Model model; View view; public Controller(Model model, View view) { this.model = model; this.view = view; view.setController(this); // 给View设置Controller引用 view.button.addActionListener(new MainButtonAction()); model.addObserver(view); } // 绑定子窗口按钮的监听器 public void bindNewWindowButton(JButton button) { button.addActionListener(new NewWindowButtonAction()); } class MainButtonAction implements ActionListener { @Override public void actionPerformed(ActionEvent e) { model.newwindow(); } } class NewWindowButtonAction implements ActionListener { @Override public void actionPerformed(ActionEvent e) { // 这里写子窗口按钮的业务逻辑,比如调用Model的方法 System.out.println("子窗口按钮被点击啦!"); } } }
调整后的Main类
public class Main { public static void main(String[] args) { Model model = new Model(); View view = new View(model); Controller controller = new Controller(model, view); } }
这样调整后,各个层的职责更清晰:
- Model:只负责维护状态和发布状态变化
- View:负责所有UI的创建、渲染,通知Controller处理用户交互
- Controller:负责绑定所有组件的监听器,处理用户输入并调用Model的业务方法
内容的提问来源于stack exchange,提问作者Lorenzo

