From aeb4db6b796a1672a0853febf643f2771a819d41 Mon Sep 17 00:00:00 2001 From: Songrui <1778280163@qq.com> Date: Fri, 29 May 2026 01:39:36 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E9=99=A4=20'target/w9-ppt.md'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- target/w9-ppt.md | 530 ----------------------------------------------- 1 file changed, 530 deletions(-) delete mode 100644 target/w9-ppt.md diff --git a/target/w9-ppt.md b/target/w9-ppt.md deleted file mode 100644 index 5ddd5ad..0000000 --- a/target/w9-ppt.md +++ /dev/null @@ -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
articles); -} -``` - ---- - -## Controller 的变革:从 switch 到 Map - -```java -public class CrawlerController { - private Map commands = new HashMap<>(); - - public CrawlerController(ConsoleView view, List
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
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
articles) { - view.printInfo("Commands: crawl , 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
articles) { - if (args.length < 2) { - view.printError("Usage: crawl "); - 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
articles) { - view.printSuccess("Bye!"); - System.exit(0); - } -} -``` - -> ✅ 所有输出都通过 View → 将来改 GUI 只需换 View 实现 - ---- - -## Controller + main 组装 - -```java -// Controller 中持有 Map -// App.java 中: -ConsoleView view = new ConsoleView(); -List
articles = new ArrayList<>(); -CrawlerController controller = new CrawlerController(view, articles); -view.printSuccess("Welcome to CLI Crawler!"); -while (true) { - controller.handle(view.readLine()); -} -``` - -> 🔁 完成交互循环 - ---- - -## 7️⃣ 架构反思:共享 List
的隐患 - -### 当前问题 - -- 所有 Command 都直接拿到 `List
` 引用 -- 任何一个命令都可以随意增、删、改列表 -- 数据完全“裸奔” - -> 🚨 就像酒店所有员工都能进保险箱 - ---- - -## 提问 - -- 如果 `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`) -3. **AI 架构审计**:将类名发给 AI,指令: - > “作为Java架构审计师,请检查我的MVC三层划分是否存在越权行为?” - -### 选做 - -- 命令别名(c 代替 crawl) -- URL 格式验证 -- 暗色主题(修改一处常量) -- 思考题:分析 `List
` 共享引用的风险(200字小结) - ---- - -## 🤖 AI 协同升级 - -### 架构审计师任务(必做) - -**步骤**: -1. 列出所有类名(不含方法实现) -2. 发给 AI -3. 指令:“检查 MVC 分层是否清晰,是否有越权行为” - -### 进阶探究(选做) - -> “假设我的 Command 接口中 execute 方法接收了一个 `List
` 参数,请分析这种设计在工程上有什么隐患,并给出重构建议。” - ---- - -## 📚 总结与过渡 - -### 本周成果 - -- ✅ 工程化包结构 -- ✅ MVC 分层清晰 -- ✅ Command 模式实现可扩展路由 -- ✅ 所有输出走 View,常量集中管理 - -### 下周预告 - -- **策略模式**:封装爬取算法 -- **仓库层(Repository)**:武装 `List
`,解决共享隐患 - -> 🚀 从“写代码”到“造系统”,踏出坚实第一步! - ---- - -## Q&A - -### 常见问题 - -| 问题 | 解答 | -|------|------| -| IDEA 不识别 pom.xml | 右键 → Maven → Reload Project | -| 中文乱码 | Settings → File Encodings → UTF-8 | -| 输出颜色乱码 | Windows 建议使用 Windows Terminal | -| 我的 System.out 被批评 | View 才是唯一输出出口 | - ---- - -## 谢谢! - -### 课件已上传,模板在课程群 - -**保持工程洁癖,下周见!** \ No newline at end of file