不使用JavaSwing的Java MVC模式菜单各层职责划分问题咨询
问题解决方案
要保证MVC三层职责不越界,核心是通过接口抽象+依赖注入的方式,把Model层的IO操作全部抽离,由View层实现IO能力,Model仅依赖抽象接口不感知具体实现。
具体实现方案
- 首先定义公共IO交互接口,封装所有打印、输入获取操作,接口定义不绑定任何具体实现,示例代码如下:
public interface MenuIOHandler { // 打印输出方法 void print(String content); // 带提示的用户输入获取方法 String getInput(String tip); }
- View层仅负责实现上述
MenuIOHandler接口,所有IO操作全部收拢在View层,不包含任何业务逻辑判断:
public class ConsoleMenuView implements MenuIOHandler { private final Scanner scanner = new Scanner(System.in); @Override public void print(String content) { // 仅实现控制台打印逻辑 System.out.println(content); } @Override public String getInput(String tip) { print(tip); // 仅实现控制台输入获取逻辑 return scanner.nextLine(); } }
- Model层的Runnable任务不再直接调用
System.out、Scanner等控制台IO类,改为依赖注入MenuIOHandler实例,所有IO需求都通过该实例实现,Model层完全不需要关心IO的具体实现逻辑:
// Model层任务示例 public class UserInfoQueryTask implements Runnable { // 依赖抽象IO接口,不绑定具体实现 private final MenuIOHandler ioHandler; // 业务参数 private final String userId; // 构造方法注入IO实例和业务参数 public UserInfoQueryTask(MenuIOHandler ioHandler, String userId) { this.ioHandler = ioHandler; this.userId = userId; } @Override public void run() { // 核心业务逻辑:查询用户信息 UserInfo userInfo = queryUserFromDB(userId); // 打印需求调用注入的IO实例 ioHandler.print("用户基础信息:" + userInfo.toString()); // 输入需求调用注入的IO实例 String targetField = ioHandler.getInput("请输入要查询的扩展字段名:"); // 后续业务逻辑处理... } }
- Controller层仅负责调度组装:拿到用户输入后,从Model层获取对应任务,把View实现的
MenuIOHandler实例注入到任务中,再触发任务执行即可,不需要介入业务逻辑也不需要处理具体IO。
职责划分验证
改造后三层完全符合单一职责原则:
- Model层:仅包含业务逻辑,可脱离控制台做单元测试(测试时注入Mock的IOHandler即可模拟输入输出)
- View层:仅负责IO操作,无任何业务耦合,后续要更换输出方式(比如改成文件输出)只需要修改View层的实现即可
- Controller层:仅负责流程调度、参数传递、实例组装,无多余逻辑
内容的提问来源于stack exchange,提问作者Stefano
相关产品推荐
相关产品推荐

