5 changed files with 183 additions and 0 deletions
@ -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 + '\'' + |
||||
|
'}'; |
||||
|
} |
||||
|
} |
||||
@ -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<Article> articles) { |
||||
|
if (args.length < 2) { |
||||
|
view.printError("Usage: crawl <url>"); |
||||
|
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); |
||||
|
} |
||||
|
} |
||||
@ -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<String, Command> commands = new HashMap<>(); |
||||
|
private final ConsoleView view; |
||||
|
private final List<Article> articles; |
||||
|
private final List<String> commandHistory = new ArrayList<>(); |
||||
|
|
||||
|
public CrawlerController(ConsoleView view, List<Article> 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); |
||||
|
} |
||||
|
} |
||||
@ -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<String> history; |
||||
|
|
||||
|
public HistoryCommand(ConsoleView view, List<String> history) { |
||||
|
this.view = view; |
||||
|
this.history = history; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String getName() { |
||||
|
return "history"; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void execute(String[] args, List<Article> 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)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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增加权限预检(如只读/读写分离) |
||||
Loading…
Reference in new issue