Browse Source

删除 'target/w9-ppt.md'

main
Songrui 3 weeks ago
parent
commit
aeb4db6b79
  1. 530
      target/w9-ppt.md

530
target/w9-ppt.md

@ -1,530 +0,0 @@
## 高级程序设计 · 第9周
#### 工程架构:从"写代码"到"造系统"
##### CLI + MVC + Command模式实战
---
### 📌 本周导航
- 痛点引入:脚本的宿命
- CLI vs GUI:为什么选命令行?
- MVC分层:职责分离的艺术
- Command模式:可扩展的路由
- Maven模板:工程化第一步
- 代码落地:从接口到实现
- 架构反思:共享数据的隐患
- 实践任务 + 课后作业
---
### 1️⃣ 痛点引入:从脚本到工程的鸿沟
#### 这是一段“意大利面”爬虫
```java
public class Crawler {
public static void main(String[] args) {
System.out.print("请输入URL: ");
Scanner scanner = new Scanner(System.in);
String url = scanner.nextLine();
List titles = new ArrayList();
try {
Document doc = Jsoup.connect(url).get();
Elements elements = doc.select(".post-title");
for (Element e : elements) {
String title = e.text();
System.out.println("标题: " + title);
titles.add(title);
}
} catch (Exception ex) {
System.out.println("出错啦: " + ex.getMessage());
}
}
}
```
---
### 脚本的三大痛点
| 需求 | 需要改哪里? |
|------|--------------|
| 保存标题到文件 | 改 main 内部逻辑 |
| 支持不同网站结构 | 全部重写解析代码 |
| 彩色输出 | 一个一个改 print |
> 😫 **牵一发而动全身 → 改起来疼**
### 本周目标:**让代码“改起来不疼”**
---
## 2️⃣ CLI vs GUI:架构选择的思考
### 图形界面 vs 命令行
| 维度 | GUI (JavaFX) | CLI (命令行) |
|------|--------------|-------------|
| 学习重心 | 布局、控件、事件 | **架构、分层、路由** |
| 后端能力 | 弱 | 模拟真实服务器 |
| 工程思维 | 弱(关注视觉) | **强(关注逻辑)** |
| 可测试性 | 难 | 易 |
---
## 核心观点
> **CLI 更需要 MVC!**
- GUI 有现成事件系统,框架强塞给你一套架构
- CLI 只有字符流 → **没有架构,分分钟写成脚本**
> 🎯 **当外部约束消失,内部的工程纪律才真正开始建立**
### CLI 也能很酷
- ANSI 彩色输出
- 表格展示数据
- 模拟大数据/后端开发
---
## 3️⃣ MVC 分层设计
### MVC 的起源与演进
| 年代 | 场景 | MVC的角色 |
|------|------|----------|
| 1970s | Smalltalk-72 GUI | 最早的用户界面架构 |
| 1990s | Web开发 (Struts) | 后端模板引擎 |
| 2000s | ASP.NET MVC | 现代Web框架 |
| 2020s | CLI + API | 解耦业务逻辑与表现层 |
**核心不变:职责分离**
---
## MVC 三层职责
![[mvc.png]]
```
┌─────────────────────────────────────────┐
│ 入口 │
│ (main方法) │
└─────────────────┬───────────────────────┘
┌─────────────────────────────────────────┐
│ Controller │
│ 只管"派给谁",不管"怎么做" │
└─────────┬───────────────┬───────────────┘
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Model │ │ View │
│ 管"数据" │ │ 管"呈现" │
│ + 业务逻辑 │ │ + 输入输出 │
└─────────────────┘ └─────────────────┘
```
---
## 三层“禁止做什么”
| 层级 | 禁止行为 |
| -------------- | -------------------------------------- |
| **Model** | 不能有 `System.out.println`,不能有 `Scanner` |
| **View** | 不能写爬虫逻辑,只做“传声筒” |
| **Controller** | 不能直接写业务细节,委托给 Command |
> 🔴 **越权就是架构腐败的开始**
---
## 🍽️ 餐厅类比(帮助理解)
- **Model = 后厨**:只管做菜,不管谁来吃、怎么端
- **View = 服务员**:只管端菜和收钱,不管菜怎么做
- **Controller = 前台**:接单 → 派给后厨 → 叫服务员上菜
---
## 🤔 对类比的批判性思考(关键!)
> 任何类比都有边界,不要当成真理
| 场景 | 暴露的问题 |
|------|------------|
| 客人有忌口(不吃香菜) | 信息需要传到后厨 → Model 可能需要知道 meta 信息 |
| 服务员反馈“今天的菜咸了” | View → Model 反向影响 |
| 后厨做完菜通知前台 | **观察者模式**,数据流可能是双向的 |
**本课程简化模型**:请求-响应,单向流
---
## MVC 数据流向(本课程简化版)
```
CLI用户输入
View(解析命令字符串)
Controller(找到对应Command)
Command.execute()(执行业务逻辑)
Model(Article数据,暂存于List)
View(display()展示数据)
CLI终端显示
```
---
## 4️⃣ Command 模式:可扩展的命令路由
### 为什么需要 Command 模式?
```java
switch (cmd) {
case "crawl": handleCrawl(); break;
case "help": showHelp(); break;
// 如果要增加 list 命令?
// 1. 加 case "list"
// 2. 加 handleList() 方法
// 3. 可能还要改其他地方...
}
```
> 每加一个功能,就要在这个类里戳一个洞 → **肥控制器陷阱**
---
## Command 模式的四个要素
| 要素 | 角色 | 示例 |
|------|------|------|
| Command接口 | 抽象的“订单” | `Command` |
| ConcreteCommand | 具体的订单 | `HelpCommand` |
| Invoker | 接单的前台 | `CrawlerController` |
| Receiver | 执行者 | `ConsoleView`、`ArticleRepository` |
---
## Command 接口定义
```java
package com.crawler.command;
import com.crawler.model.Article;
import java.util.List;
public interface Command {
String getName();
void execute(String[] args, List<Article> articles);
}
```
---
## Controller 的变革:从 switch 到 Map
```java
public class CrawlerController {
private Map<String, Command> commands = new HashMap<>();
public CrawlerController(ConsoleView view, List<Article> articles) {
commands.put("help", new HelpCommand(view));
commands.put("list", new ListCommand(view));
commands.put("crawl", new CrawlCommand(view));
commands.put("exit", new ExitCommand(view));
}
public void handle(String input) {
// 解析命令 → 从 Map 取 Command → 调用 execute
}
}
```
> **增加新命令:只需新建类,Controller 零改动!**
---
## 对比:switch-case vs Command
| 维度 | switch-case | Command模式 |
|------|-------------|-------------|
| 增加命令 | 要改 Controller | 新建一个类 |
| 多态体验 | 无 | `execute()` 多态 |
| 可测试性 | 难 | 每个 Command 单独测试 |
| 代码量 | 少 | 多,但更清晰 |
> 🏨 **类比:酒店客房服务,前台只负责派单**
---
## 5️⃣ Maven 模板与环境(5分钟)
### 直接使用模板,不折腾配置
```
my-crawler-template.zip
↓ 解压 + IDEA打开
↓ 右键 pom.xml → Maven → Reload Project
↓ 运行 App.java
```
### 标准目录结构
```
src/main/java/com/crawler/
├── model/Article.java
├── view/ConsoleView.java
├── command/
│ ├── Command.java
│ ├── CrawlCommand.java
│ ├── HelpCommand.java
│ ├── ListCommand.java
│ └── ExitCommand.java
└── controller/CrawlerController.java
```
---
## 6️⃣ 代码落地(分步实现)
### Model:Article 实体
```java
public class Article {
private String title;
private String url;
private String content;
// 构造器、getter/setter、toString
}
```
> 📦 只存放数据,没有任何输入输出代码
---
## View:ConsoleView(ANSI常量集中管理)
```java
public class ConsoleView {
private static final String ANSI_GREEN = "\033[32m";
private static final String ANSI_RED = "\033[31m";
// ... 其他常量
public void printSuccess(String msg) {
System.out.println(ANSI_GREEN + msg + ANSI_RESET);
}
public void printError(String msg) { ... }
public void display(List<Article> articles) { ... }
}
```
> ✨ **所有颜色码集中定义 → 改主题只需改一处**
---
## Command 实现示例(HelpCommand)
```java
public class HelpCommand implements Command {
private ConsoleView view;
public HelpCommand(ConsoleView v) { this.view = v; }
public String getName() { return "help"; }
public void execute(String[] args, List<Article> articles) {
view.printInfo("Commands: crawl <url>, list, help, exit");
}
}
```
> ⚠️ 全部输出通过 `view`,绝不让 `System.out` 直接出现在这里
---
## CrawlCommand(存根,下周填坑)
```java
public class CrawlCommand implements Command {
private ConsoleView view;
public CrawlCommand(ConsoleView v) { this.view = v; }
public String getName() { return "crawl"; }
public void execute(String[] args, List<Article> articles) {
if (args.length < 2) {
view.printError("Usage: crawl <url>");
return;
}
view.printInfo("Stub: Would crawl " + args[1]);
}
}
```
> 🔍 **找茬点**:这里拼接字符串算是“业务逻辑”吗?留给大家用 AI 审计。
---
## ExitCommand
```java
public class ExitCommand implements Command {
private ConsoleView view;
public ExitCommand(ConsoleView v) { this.view = v; }
public String getName() { return "exit"; }
public void execute(String[] args, List<Article> articles) {
view.printSuccess("Bye!");
System.exit(0);
}
}
```
> ✅ 所有输出都通过 View → 将来改 GUI 只需换 View 实现
---
## Controller + main 组装
```java
// Controller 中持有 Map<String,Command>
// App.java 中:
ConsoleView view = new ConsoleView();
List<Article> articles = new ArrayList<>();
CrawlerController controller = new CrawlerController(view, articles);
view.printSuccess("Welcome to CLI Crawler!");
while (true) {
controller.handle(view.readLine());
}
```
> 🔁 完成交互循环
---
## 7️⃣ 架构反思:共享 List<Article> 的隐患
### 当前问题
- 所有 Command 都直接拿到 `List<Article>` 引用
- 任何一个命令都可以随意增、删、改列表
- 数据完全“裸奔”
> 🚨 就像酒店所有员工都能进保险箱
---
## 提问
- 如果 `CrawlCommand` 不小心把 `null` 塞进列表,`ListCommand` 会怎样?
- 如果我们要在添加文章时写日志,现在的设计能优雅实现吗?
### 预告解决方案(W10)
- **策略模式** + **仓库层(ArticleRepository)**
- 封装 `List`,对外只暴露 `add()`、`getAll()` 等安全接口
> W9 搭骨架,W10 装上盔甲
---
## 8️⃣ 实践任务(现场5分钟)
### 必做项
1. 使用 Maven 模板创建项目
2. 实现完整包结构(model/view/command/controller)
3. 实现 4 个 Command:help / list / crawl / exit
4. `list` 能展示已抓取的文章(目前存根即可)
5. 运行并测试循环
### 额外加分:代码找茬
- 检查是否仍有 `System.out` 直接调用
- 检查 ANSI 码是否硬编码在多个地方
---
## 验收标准
- [x] Maven 编译通过
- [x] Command 接口和 4 个实现在不同文件
- [x] Controller 里没有 switch-case
- [x] 新增命令只需新建类,不改 Controller
- [x] list 能正确显示空列表
- [x] 所有输出均通过 `ConsoleView`
- [x] ANSI 颜色码集中定义为常量
---
## 9️⃣ 课后作业
### 必做
1. **完善 Article**:增加 `author`、`publishDate` 字段
2. **★ HistoryCommand**:记录用户输入过的所有命令(用 `List<String>`
3. **AI 架构审计**:将类名发给 AI,指令:
> “作为Java架构审计师,请检查我的MVC三层划分是否存在越权行为?”
### 选做
- 命令别名(c 代替 crawl)
- URL 格式验证
- 暗色主题(修改一处常量)
- 思考题:分析 `List<Article>` 共享引用的风险(200字小结)
---
## 🤖 AI 协同升级
### 架构审计师任务(必做)
**步骤**:
1. 列出所有类名(不含方法实现)
2. 发给 AI
3. 指令:“检查 MVC 分层是否清晰,是否有越权行为”
### 进阶探究(选做)
> “假设我的 Command 接口中 execute 方法接收了一个 `List<Article>` 参数,请分析这种设计在工程上有什么隐患,并给出重构建议。”
---
## 📚 总结与过渡
### 本周成果
- ✅ 工程化包结构
- ✅ MVC 分层清晰
- ✅ Command 模式实现可扩展路由
- ✅ 所有输出走 View,常量集中管理
### 下周预告
- **策略模式**:封装爬取算法
- **仓库层(Repository)**:武装 `List<Article>`,解决共享隐患
> 🚀 从“写代码”到“造系统”,踏出坚实第一步!
---
## Q&A
### 常见问题
| 问题 | 解答 |
|------|------|
| IDEA 不识别 pom.xml | 右键 → Maven → Reload Project |
| 中文乱码 | Settings → File Encodings → UTF-8 |
| 输出颜色乱码 | Windows 建议使用 Windows Terminal |
| 我的 System.out 被批评 | View 才是唯一输出出口 |
---
## 谢谢!
### 课件已上传,模板在课程群
**保持工程洁癖,下周见!**
Loading…
Cancel
Save