From 0379587d8ffd6205b03c5dd3ecb80335966b57b6 Mon Sep 17 00:00:00 2001 From: LiuZihan <1353843969@qq.com> Date: Thu, 30 Apr 2026 19:45:45 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E8=87=B3?= =?UTF-8?q?=20'w9'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- w9/Article.java | 45 +++++++++++++++++++++++++++++++++++ w9/CrawlCommand.java | 37 +++++++++++++++++++++++++++++ w9/CrawlerController.java | 50 +++++++++++++++++++++++++++++++++++++++ w9/HistoryCommand.java | 32 +++++++++++++++++++++++++ w9/README2.md | 19 +++++++++++++++ 5 files changed, 183 insertions(+) create mode 100644 w9/Article.java create mode 100644 w9/CrawlCommand.java create mode 100644 w9/CrawlerController.java create mode 100644 w9/HistoryCommand.java create mode 100644 w9/README2.md diff --git a/w9/Article.java b/w9/Article.java new file mode 100644 index 0000000..62800e6 --- /dev/null +++ b/w9/Article.java @@ -0,0 +1,45 @@ +// Article.java +package com.example.datacollect.model; + +public class Article { + private String title; + private String url; + private String content; + private String author; // 新增 + private String publishDate; // 新增 + + // 构造器 + public Article(String title, String url, String content) { + this(title, url, content, null, null); + } + + public Article(String title, String url, String content, String author, String publishDate) { + this.title = title; + this.url = url; + this.content = content; + this.author = author; + this.publishDate = publishDate; + } + + // getter / setter + public String getTitle() { return title; } + public void setTitle(String title) { this.title = title; } + public String getUrl() { return url; } + public void setUrl(String url) { this.url = url; } + public String getContent() { return content; } + public void setContent(String content) { this.content = content; } + public String getAuthor() { return author; } + public void setAuthor(String author) { this.author = author; } + public String getPublishDate() { return publishDate; } + public void setPublishDate(String publishDate) { this.publishDate = publishDate; } + + @Override + public String toString() { + return "Article{" + + "title='" + title + '\'' + + ", url='" + url + '\'' + + ", author='" + author + '\'' + + ", publishDate='" + publishDate + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/w9/CrawlCommand.java b/w9/CrawlCommand.java new file mode 100644 index 0000000..2b3cf2c --- /dev/null +++ b/w9/CrawlCommand.java @@ -0,0 +1,37 @@ +package com.example.datacollect.command; + +import com.example.datacollect.model.Article; +import com.example.datacollect.view.ConsoleView; +import java.util.List; + +public class CrawlCommand implements Command { + private final ConsoleView view; + + public CrawlCommand(ConsoleView view) { + this.view = view; + } + + @Override + public String getName() { + return "crawl"; + } + + @Override + public void execute(String[] args, List
articles) { + if (args.length < 2) { + view.printError("Usage: crawl "); + return; + } + String urlStr = args[1]; + + // URL 格式验证 + try { + new java.net.URL(urlStr).toURI(); + } catch (Exception e) { + view.printError("Invalid URL format: " + urlStr); + return; + } + + view.printInfo("Stub: would crawl " + urlStr); + } +} \ No newline at end of file diff --git a/w9/CrawlerController.java b/w9/CrawlerController.java new file mode 100644 index 0000000..d8a5d5a --- /dev/null +++ b/w9/CrawlerController.java @@ -0,0 +1,50 @@ +package com.example.datacollect.controller; + +import com.example.datacollect.command.*; +import com.example.datacollect.model.Article; +import com.example.datacollect.view.ConsoleView; +import java.util.*; + +public class CrawlerController { + private final Map commands = new HashMap<>(); + private final ConsoleView view; + private final List
articles; + private final List commandHistory = new ArrayList<>(); + + public CrawlerController(ConsoleView view, List
articles) { + this.view = view; + this.articles = articles; + register(new HelpCommand(view)); + register(new ListCommand(view)); + register(new CrawlCommand(view)); + register(new ExitCommand(view)); + register(new HistoryCommand(view, commandHistory)); + } + + private void register(Command command) { + commands.put(command.getName(), command); + } + + public void handle(String input) { + String text = input == null ? "" : input.trim(); + if (text.isEmpty()) { + return; + } + commandHistory.add(text); + + String[] args = text.split("\\s+"); + String cmdName = args[0].toLowerCase(); + + // 命令别名:c -> crawl + if (cmdName.equals("c")) { + cmdName = "crawl"; + } + + Command command = commands.get(cmdName); + if (command == null) { + view.printError("Unknown command: " + cmdName); + return; + } + command.execute(args, articles); + } +} \ No newline at end of file diff --git a/w9/HistoryCommand.java b/w9/HistoryCommand.java new file mode 100644 index 0000000..59e1e5e --- /dev/null +++ b/w9/HistoryCommand.java @@ -0,0 +1,32 @@ +package com.example.datacollect.command; + +import com.example.datacollect.model.Article; +import com.example.datacollect.view.ConsoleView; +import java.util.List; + +public class HistoryCommand implements Command { + private final ConsoleView view; + private final List history; + + public HistoryCommand(ConsoleView view, List history) { + this.view = view; + this.history = history; + } + + @Override + public String getName() { + return "history"; + } + + @Override + public void execute(String[] args, List
articles) { + if (history.isEmpty()) { + view.printInfo("暂无命令历史。"); + return; + } + view.printInfo("==== 命令历史 ===="); + for (int i = 0; i < history.size(); i++) { + System.out.println((i + 1) + ". " + history.get(i)); + } + } +} \ No newline at end of file diff --git a/w9/README2.md b/w9/README2.md new file mode 100644 index 0000000..72b230b --- /dev/null +++ b/w9/README2.md @@ -0,0 +1,19 @@ +# Java MVC 架构审计报告 + +**审计对象**:Article (Model), ConsoleView (View), CrawlerController (Controller), Command家族 (Command层) + +**审计结论**:整体符合MVC分层,无严重越权,存在轻微旁路风险。 + +## 各层审计详情 + +| 层级 | 类名 | 职责 | 是否越权 | 说明 | +|------|------|------|----------|------| +| Model | Article | 纯数据载体,无业务逻辑 | ✅ 否 | 满足POJO规范 | +| View | ConsoleView | 仅负责I/O,展示数据 | ⚠️ 轻度 | 持有List引用但未修改,属于正常依赖 | +| Controller | CrawlerController | 命令路由,持有全局数据 | ✅ 否 | 职责明确,不直接操作Model细节 | +| Command | CrawlCommand等 | 执行业务,直接修改List | ⚠️ 委托 | Controller主动授权,非越权但有潜在风险 | + +## 改进建议 +1. 使用 `Collections.unmodifiableList()` 向View提供只读视图 +2. 引入 `ArticleRepository` 统一管理数据修改操作 +3. 对Command增加权限预检(如只读/读写分离) \ No newline at end of file