|
|
|
@ -15,3 +15,121 @@ 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<Article>`,如果未来要存数据库或文件,所有命令都要改。 |
|
|
|
* **优化**: |
|
|
|
* 新增 `DataService` 接口(定义 `save`, `get`, `remove` 等方法)。 |
|
|
|
* 实现 `ArticleRepository` 类实现 `DataService`。 |
|
|
|
* **修改 `Command` 接口**:参数从 `List<Article>` 改为 `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<Article>` | `Command` 依赖 `DataService` 接口 | |
|
|
|
| **数据持久化** | ❌ 内存数据,重启丢失 | ✅ 支持 `save/load` 到文件 | |
|
|
|
| **查询能力** | ❌ 仅能列出全部 | ✅ 支持 `search` 关键词筛选 | |
|
|
|
| **管理能力** | ❌ 无法删除 | ✅ 支持 `delete` 按索引删除 | |
|
|
|
| **列表显示** | ❌ 全部显示,可能刷屏 | ✅ 支持 `list <num>` 分页显示 | |
|
|
|
| **错误处理** | ❌ 简单打印,易崩溃 | ✅ 参数校验,异常捕获 | |
|
|
|
| **扩展性** | ❌ 新增功能需改多处 | ✅ 新增命令无需改 Controller | |
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
### 🛠️ 核心代码变化速览 |
|
|
|
|
|
|
|
1. **接口定义变化**: |
|
|
|
```java |
|
|
|
// 旧 |
|
|
|
void execute(String[] args, List<Article> articles); |
|
|
|
// 新 |
|
|
|
void execute(String[] args, DataService dataService); |
|
|
|
``` |
|
|
|
|
|
|
|
2. **控制器注入变化**: |
|
|
|
```java |
|
|
|
// 旧 |
|
|
|
public CrawlerController(ConsoleView view, List<Article> articles) |
|
|
|
// 新 |
|
|
|
public CrawlerController(ConsoleView view, DataService dataService) |
|
|
|
``` |
|
|
|
|
|
|
|
3. **视图方法变化**: |
|
|
|
```java |
|
|
|
// 旧 |
|
|
|
void display(List<Article> articles) |
|
|
|
// 新 |
|
|
|
void display(List<Article> 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`)。 |
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
|