# DataCollect 教学项目 — 最小可运行版本 这是一个最小可用的 Java CLI 演示工程,目标:打印帮助信息以验证运行环境。 构建: ```bash mvn -q package ``` 运行(示例): ```bash java -jar target/datacollect-cli-0.1.0-jar-with-dependencies.jar --help ``` 项目结构(最小): - `src/main/java/com/example/datacollect/Main.java` — CLI 入口,打印帮助 - `pom.xml` — Maven 构建配置,生成可执行 jar 这是一个非常完整的 **Java CLI 应用从“可用”到“健壮”的演进过程**。我们将整个过程总结为 **5 个核心阶段**,涵盖了架构设计、代码重构、功能扩展、用户体验优化以及设计原则的落地。 --- ### 整个优化流程总结 #### 1. 初始阶段:MVC + 命令模式架构搭建 **目标**:让程序跑起来,结构清晰。 * **架构**:采用 **MVC (Model-View-Controller)** 结合 **命令模式 (Command Pattern)**。 * **核心类**: * `Model`: `Article` (数据实体)。 * `View`: `ConsoleView` (输入输出,颜色美化)。 * `Controller`: `CrawlerController` (命令分发)。 * `Command`: `Command` 接口 + 具体实现 (`CrawlCommand`, `ListCommand` 等)。 * **数据流**:用户输入 → Controller 解析 → 调用 Command → 操作数据 → 输出结果。 * **状态**:✅ 功能可用,✅ 结构清晰,❌ 耦合度高,❌ 扩展性差。 #### 2. 重构阶段:引入 `DataService` 解耦 **目标**:解决 `Command` 直接依赖 `List` 的问题,实现依赖倒置。 * **痛点**:原代码中 `Command` 直接持有 `List
`,如果未来要存数据库或文件,所有命令都要改。 * **优化**: * 新增 `DataService` 接口(定义 `save`, `get`, `remove` 等方法)。 * 实现 `ArticleRepository` 类实现 `DataService`。 * **修改 `Command` 接口**:参数从 `List
` 改为 `DataService`。 * **修改 `Controller`**:注入 `DataService` 而非 `List`。 * **收益**:✅ **开闭原则**(新增存储方式无需改命令),✅ **易于测试**(可 Mock `DataService`)。 #### 3. 功能扩展阶段:完善 CRUD 与持久化 **目标**:让应用真正具备生产力,不仅仅是演示。 * **新增命令**: * `SearchCommand`:支持关键词搜索(查)。 * `DeleteCommand`:支持按索引删除(改)。 * `SaveCommand` / `LoadCommand`:支持文件读写(持久化)。 * **数据完整性**:确保 `CrawlCommand` 真正将数据存入 `DataService`,而非仅打印日志。 * **收益**:✅ **功能闭环**(增删改查全),✅ **数据安全**(重启不丢失)。 #### 4. 体验优化阶段:增强交互与容错 **目标**:提升用户命令行体验,减少错误操作。 * **`ListCommand` 优化**: * 增加 `limit` 参数(如 `list 10`),防止刷屏。 * 增加分页提示(“显示前 20 条,共 50 条”)。 * 增加空列表友好提示。 * **参数校验**:所有命令增加参数数量检查(如 `crawl` 必须带 URL)。 * **异常处理**:增加 `try-catch` 处理文件 IO 异常和数字格式异常。 * **收益**:✅ **用户体验好**(防刷屏、提示清晰),✅ **健壮性强**(不轻易崩溃)。 #### 5. 最终架构:标准化与最佳实践 **目标**:形成可维护、可扩展的企业级 CLI 雏形。 * **包结构**: ```text src/ ├── model/ (Article, DataService, ArticleRepository) ├── command/ (Command 接口,所有具体命令) ├── controller/ (CrawlerController) ├── view/ (ConsoleView) └── Main.java (入口) ``` * **设计原则**: * **单一职责**:每个类只做一件事。 * **依赖倒置**:依赖接口 `DataService` 而非具体 `List`。 * **开闭原则**:新增命令只需实现 `Command` 接口,无需修改 `Controller`。 --- ### 📊 优化前后对比表 | 维度 | 优化前 (V1) | 优化后 (V2) | | :--- | :--- | :--- | | **数据依赖** | `Command` 直接依赖 `List
` | `Command` 依赖 `DataService` 接口 | | **数据持久化** | ❌ 内存数据,重启丢失 | ✅ 支持 `save/load` 到文件 | | **查询能力** | ❌ 仅能列出全部 | ✅ 支持 `search` 关键词筛选 | | **管理能力** | ❌ 无法删除 | ✅ 支持 `delete` 按索引删除 | | **列表显示** | ❌ 全部显示,可能刷屏 | ✅ 支持 `list ` 分页显示 | | **错误处理** | ❌ 简单打印,易崩溃 | ✅ 参数校验,异常捕获 | | **扩展性** | ❌ 新增功能需改多处 | ✅ 新增命令无需改 Controller | --- ### 🛠️ 核心代码变化速览 1. **接口定义变化**: ```java // 旧 void execute(String[] args, List
articles); // 新 void execute(String[] args, DataService dataService); ``` 2. **控制器注入变化**: ```java // 旧 public CrawlerController(ConsoleView view, List
articles) // 新 public CrawlerController(ConsoleView view, DataService dataService) ``` 3. **视图方法变化**: ```java // 旧 void display(List
articles) // 新 void display(List
articles, int totalSize) ``` --- ### 🎓 设计原则总结 在这个优化过程中,我们实际上实践了以下 **SOLID 原则**: 1. **S (Single Responsibility)**:`ConsoleView` 只管显示,`DataService` 只管数据,`Command` 只管逻辑。 2. **O (Open/Closed)**:新增 `DeleteCommand` 时,不需要修改 `CrawlerController` 的 `handle` 方法。 3. **L (Liskov Substitution)**:任何实现 `DataService` 的类(如 `FileService`, `DBService`)都可以替换 `ArticleRepository`。 4. **I (Interface Segregation)**:`DataService` 接口只定义了必要的数据操作方法,没有混杂 UI 逻辑。 5. **D (Dependency Inversion)**:高层模块 (`Controller`, `Command`) 依赖抽象 (`DataService`),而非具体实现 (`ArrayList`)。 ---