You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6.0 KiB
6.0 KiB
作为 Java 架构审计师,基于您提供的代码库(包含核心任务及选做任务的实现),以下是针对 策略模式解耦、Repository 封装 及 工厂性能 的深度审计报告。
📋 架构审计报告
1. 策略模式解耦性审计 (Strategy Pattern Decoupling)
✅ 审计结论:基本正确,符合开闭原则。
- 现状分析:
CrawlCommand和AnalyzeCommand均通过StrategyFactory获取策略,并未在命令内部硬编码if (url.contains("hnu"))等具体网站逻辑。- 解析逻辑完全下沉到
HnuNewsStrategy等具体策略类中。 AnalyzeCommand复用了CrawlStrategy的parse方法,实现了“一次实现,多处复用”的目标。
- 潜在风险:
- 如果
supports()方法内部包含复杂的 URL 解析逻辑,可能会污染策略接口。 AnalyzeCommand目前虽然不存数据,但如果未来需要“分析后自动保存”,它可能会直接调用repository,导致职责不清。
- 如果
- 改进建议:
- 保持现状:目前的解耦设计是合理的,无需大改。
- 接口纯净化:确保
CrawlStrategy接口中不包含任何与“存储”相关的逻辑,只关注“解析”。 - 异常隔离:在
CrawlCommand中捕获Jsoup异常时,建议区分网络异常和解析异常,以便策略层能针对性处理(例如重试或跳过)。
2. Repository 数据访问封装审计 (Repository Encapsulation)
⚠️ 审计结论:封装性良好,但存在数据模型可变性风险。
- 现状分析:
ArticleRepository内部持有List<Article>,外部无法直接获取引用(getAll()返回Collections.unmodifiableList)。addAll方法增加了null检查,符合防御性编程。- 命令层(
CrawlCommand)通过repository.add()操作数据,没有绕过 Repository 直接操作 List。
- 潜在风险:
- 模型可变性:
Article类提供了setTitle,setContent等 Setter 方法。这意味着外部获取到Article对象后,可以修改其状态,破坏了 Repository 的数据一致性封装。 - 线程安全:
ArticleRepository内部使用ArrayList,在多线程环境下(如未来扩展并发爬虫)存在ConcurrentModificationException风险。 - 资源泄露:
ConsoleView中的Scanner未关闭,虽然程序退出时 OS 会回收,但规范上应实现AutoCloseable。
- 模型可变性:
- 改进建议:
- 不可变模型:将
Article类改为 不可变类 (Immutable)。移除所有 Setter 方法,构造函数中初始化所有字段。 - 线程安全:将
ArticleRepository内部 List 替换为CopyOnWriteArrayList或Collections.synchronizedList,或者在 Repository 层面加锁。 - 资源管理:
ConsoleView实现AutoCloseable接口,在Main中通过try-with-resources关闭 Scanner。
- 不可变模型:将
3. 策略工厂匹配逻辑性能审计 (Factory Performance)
❌ 审计结论:存在显著性能隐患,不适合大规模扩展。
- 现状分析:
- 在
StrategyFactory.getStrategy()中,使用了strategies.stream().sorted(...).filter(...)。 - 性能隐患:每次调用
getStrategy(即每次爬虫请求)都会执行一次 排序操作 (O(N log N))。如果网站数量增加到 100 个,且用户频繁调用crawl命令,这会带来不必要的 CPU 开销。 - 匹配逻辑:目前依赖
supports()的线性遍历。如果 URL 规则复杂,正则匹配本身也有开销。
- 在
- 改进建议:
- 预排序 (Pre-sorting):
- 方案:在
StrategyFactory的 构造函数 中完成排序,而不是在getStrategy方法中。 - 代码:
public StrategyFactory() { strategies.add(new HnuNewsStrategy()); // ... Collections.sort(strategies, (s1, s2) -> Integer.compare(s2.getPriority(), s1.getPriority())); } - 方案:在
- 路由优化 (Routing Optimization):
- 如果网站数量超过 50 个,线性遍历 (
O(N)) 效率会下降。 - 方案:使用
Map<String, CrawlStrategy>进行前缀匹配,或使用 Trie 树 (字典树) 存储 URL 域名前缀。 - 示例:将
blog.example.com和news.hnu.edu.cn作为 Key 存入 Map,匹配时直接map.get(url),将复杂度降为O(1)。
- 如果网站数量超过 50 个,线性遍历 (
- 正则缓存:确保
Pattern对象是static final的(当前代码已做到),避免重复编译正则表达式。
- 预排序 (Pre-sorting):
🚀 综合改进建议清单 (Action Items)
| 优先级 | 模块 | 问题描述 | 改进方案 |
|---|---|---|---|
| P0 | StrategyFactory |
getStrategy 中每次调用都排序 |
移至构造函数排序,消除 O(N log N) 重复计算。 |
| P0 | Article |
存在 Setter 方法,数据可被篡改 | 移除 Setter,改为全参构造函数,实现不可变对象。 |
| P1 | ArticleRepository |
ArrayList 非线程安全 |
若需并发,改为 CopyOnWriteArrayList 或加锁。 |
| P1 | ConsoleView |
Scanner 未关闭 |
实现 AutoCloseable 并在 Main 中关闭。 |
| P2 | StrategyFactory |
100+ 网站时线性匹配慢 | 引入 URL 前缀映射表 或 Trie 树 优化匹配。 |
| P3 | CrawlCommand |
异常捕获过宽 | 区分 IOException (网络) 和 ParserException (解析)。 |
📝 审计总结
当前架构在 设计模式 和 分层解耦 上做得非常出色,特别是策略模式与命令模式的结合,使得系统具备极强的扩展性。
最大的瓶颈在于 StrategyFactory 的运行时性能(每次调用都排序)和 数据模型的封装性(Article 可变)。建议优先修复这两个问题,即可支撑从“教学 Demo"到“生产级爬虫系统”的跨越。