面向对象设计命令行文本组件需拆分职责:inputreader读输入,outputwriter管输出,textrenderer负责渲染,commandhandler处理命令,clirunner协调流程;各组件通过构造函数注入标准流,支持测试;命令路由用map注册,渲染与业务逻辑严格分离。

用面向对象逻辑设计命令行文本组件,核心是把“输入”“输出”“交互逻辑”拆成职责明确的对象,再通过标准输入输出流(System.in / System.out)连接它们。不封装成黑盒,而是让每个类只做一件事:读一行、格式化一段文字、执行某个命令、或协调流程。
定义清晰的组件边界
避免把所有功能塞进一个类。典型划分如下:
-
InputReader:封装
Scanner(System.in),只负责安全读取字符串、整数、确认操作(如 yes/no),屏蔽异常和空输入处理; -
OutputWriter:封装
System.out,提供带前缀(如[INFO])、缩进、颜色(ANSI 转义序列)、分隔线等基础输出能力; -
TextRenderer:接收原始数据(如 List
或 Map ),按模板渲染成对齐/换行/高亮的文本块,不碰 IO; -
CommandHandler:每个子命令(如
help、list、add)对应一个实现类,只处理业务逻辑并返回结果数据; - CliRunner:主协调者,循环读命令 → 路由到对应 Handler → 渲染结果 → 输出,不包含业务规则。
用标准流做轻量级依赖注入
不硬编码 System.in 和 System.out,而是通过构造函数传入 InputStream 和 PrintStream,方便单元测试(比如用 ByteArrayInputStream 模拟输入,ByteArrayOutputStream 捕获输出):
public class InputReader {
private final Scanner scanner;
public InputReader(InputStream in) {
this.scanner = new Scanner(in); // 可替换为测试用流
}
public String readLine() { return scanner.nextLine(); }
}
同理,OutputWriter 接收 PrintStream,调用 print() 或 println() 即可,不直接写 System.out。
命令路由与松耦合交互
避免 if-else 链判断命令字。用 Map
- 启动时注册:
router.register("help", new HelpCommand()); - 输入后截取首词作为指令名,查表获取 Handler;
- 剩余参数(如
list --all中的--all)交给 Handler 自己解析(可用简单正则或 Apache Commons CLI); - Handler 返回 POJO(如
CommandResult<list>></list>),由 CliRunner 统一交给 TextRenderer 格式化。
保持文本渲染与业务逻辑分离
例如要显示任务列表,业务层返回 List<task></task>,渲染层决定怎么排版:
- 纯文本模式:每行
[ ] 001. Buy milk; - 带状态高亮:完成项加
\u001B[32m✓\u001B[0m(绿色对勾); - 表格模式:计算最长标题宽度,用空格补全对齐;
- 关键点:渲染逻辑不访问数据库、不调 API、不改状态——它只是“画图”。
不复杂但容易忽略:真正面向对象不是套语法,而是让每个类有唯一变化原因。输入方式变(比如支持文件输入)、输出样式变(加 JSON 模式)、命令增减——这些修改都只影响对应组件,其他部分不动。











