10 changed files with 221 additions and 0 deletions
@ -0,0 +1,18 @@ |
|||||
|
检查策略解耦与封装 |
||||
|
解耦方面:策略接口、工厂、使用者三层结构清晰,使用者与具体爬取算法完全解耦,新增 / 修改网站策略不影响上层命令与主程序,符合开闭原则;仅主程序存在少量硬编码注册,不影响整体解耦效果。 |
||||
|
2. 封装方面:策略实现细节、策略集合、数据模型均做到私有隐藏、接口暴露,外部只能通过规范接口交互,有效隔离内部变化,封装完整、安全。 |
||||
|
3. 整体架构:策略模式 + 命令模式 + 工厂模式组合,职责清晰、依赖单向、无循环依赖,具备良好的可维护性与扩展性。 |
||||
|
|
||||
|
检查开闭原则达成度等 |
||||
|
审计结论: |
||||
|
1. 开闭原则:项目采用命令模式 + 策略模式,新增命令只需实现Command2接口、新增策略只需实现CrawlStrategy2接口,无需修改主程序和工厂代码,完全符合对扩展开放、对修改关闭的原则。 |
||||
|
2. Repository 封装:ArticleRepository内部集合用private final修饰,外部仅能通过add/addAll方法添加,且返回的是Collections.unmodifiableList不可修改视图,同时对null做了严格防御,封装完备,数据安全。 |
||||
|
3. 循环依赖:所有组件均通过构造方法依赖注入,依赖方向为Main2 → 命令/工厂/仓库 → 模型,无反向依赖,不存在循环依赖问题。 |
||||
|
|
||||
|
进阶探究题答案 |
||||
|
题目:不用工厂,直接用Map<String, CrawlStrategy>存起来 vs StrategyFactory的区别? |
||||
|
直接用Map存储策略,虽然实现简单,但存在明显局限: |
||||
|
1. 耦合度高:调用方需要直接维护Map的注册和匹配逻辑,新增策略时需要修改调用方代码,违反开闭原则。 |
||||
|
2. 扩展性差:无法在匹配逻辑中添加复杂规则(如优先级、默认策略、正则匹配),后续扩展困难。 |
||||
|
3. 可维护性低:匹配逻辑散落在业务代码中,难以统一管理和修改。 |
||||
|
而StrategyFactory将策略的注册、匹配逻辑封装为独立组件,调用方仅依赖工厂接口,无需关心具体实现;新增策略只需注册到工厂,不影响其他代码,既降低耦合,又提升了扩展性和可维护性,更符合面向对象设计规范。 |
||||
@ -0,0 +1,34 @@ |
|||||
|
import java.util.List; |
||||
|
|
||||
|
public class AnalyzeCommand2 implements Command2 { |
||||
|
private final StrategyFactory2 factory; |
||||
|
|
||||
|
public AnalyzeCommand2(StrategyFactory2 factory) { |
||||
|
this.factory = factory; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String getName() { |
||||
|
return "analyze"; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void execute(String[] args, List<Article2> articles) { |
||||
|
if (args == null || args.length == 0) { |
||||
|
System.out.println("格式:analyze 网址"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
String url = args[0]; |
||||
|
CrawlStrategy2 strategy = factory.getMatchStrategy(url); |
||||
|
|
||||
|
System.out.println("===== 分析结果 ====="); |
||||
|
if (strategy != null) { |
||||
|
System.out.println("链接:" + url); |
||||
|
System.out.println("状态:可以解析 ✅"); |
||||
|
} else { |
||||
|
System.out.println("链接:" + url); |
||||
|
System.out.println("状态:不支持 ❌"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
public class Article2 { |
||||
|
private String title; |
||||
|
private String url; |
||||
|
private String content; |
||||
|
private String author; |
||||
|
private String publishDate; |
||||
|
|
||||
|
public Article2(String title, String url, String content) { |
||||
|
this.title = title; |
||||
|
this.url = url; |
||||
|
this.content = content; |
||||
|
} |
||||
|
|
||||
|
public Article2(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; |
||||
|
} |
||||
|
|
||||
|
public String getTitle() { return title; } |
||||
|
public String getUrl() { return url; } |
||||
|
public String getContent() { return content; } |
||||
|
public String getAuthor() { return author; } |
||||
|
public String getPublishDate() { return publishDate; } |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
import java.util.ArrayList; |
||||
|
import java.util.Collections; |
||||
|
import java.util.List; |
||||
|
|
||||
|
public class ArticleRepository { |
||||
|
private final List<Article2> articles = new ArrayList<>(); |
||||
|
|
||||
|
public void add(Article2 article) { |
||||
|
if (article == null) throw new IllegalArgumentException("文章不能为空"); |
||||
|
articles.add(article); |
||||
|
} |
||||
|
|
||||
|
public void addAll(List<Article2> newArticles) { |
||||
|
if (newArticles == null) throw new IllegalArgumentException("列表不能为空"); |
||||
|
for (Article2 a : newArticles) { |
||||
|
if (a == null) throw new IllegalArgumentException("列表中不能有空对象"); |
||||
|
articles.add(a); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public List<Article2> getAll() { |
||||
|
return Collections.unmodifiableList(articles); |
||||
|
} |
||||
|
|
||||
|
public int size() { |
||||
|
return articles.size(); |
||||
|
} |
||||
|
|
||||
|
public void clear() { |
||||
|
articles.clear(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,6 @@ |
|||||
|
import java.util.List; |
||||
|
|
||||
|
public interface Command2 { |
||||
|
String getName(); |
||||
|
void execute(String[] args, List<Article2> articles); |
||||
|
} |
||||
@ -0,0 +1,4 @@ |
|||||
|
public interface CrawlStrategy2 { |
||||
|
boolean supports(String url); |
||||
|
Article2 crawl(String url); |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
public class HistoryCommand2 implements Command2 { |
||||
|
private final List<String> commandHistory = new ArrayList<>(); |
||||
|
|
||||
|
@Override |
||||
|
public String getName() { |
||||
|
return "history"; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void execute(String[] args, List<Article2> articles) { |
||||
|
if (commandHistory.isEmpty()) { |
||||
|
System.out.println("暂无历史记录"); |
||||
|
return; |
||||
|
} |
||||
|
System.out.println("===== 命令历史 ====="); |
||||
|
for (int i = 0; i < commandHistory.size(); i++) { |
||||
|
System.out.println((i+1) + ". " + commandHistory.get(i)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void addCommand(String cmd) { |
||||
|
commandHistory.add(cmd); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,56 @@ |
|||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
import java.util.Scanner; |
||||
|
|
||||
|
public class Main3 { |
||||
|
public static void main(String[] args) { |
||||
|
StrategyFactory2 factory = new StrategyFactory2(); |
||||
|
|
||||
|
// 模拟一个博客策略
|
||||
|
CrawlStrategy2 blogStrategy = new CrawlStrategy2() { |
||||
|
@Override |
||||
|
public boolean supports(String url) { |
||||
|
return url.contains("blog"); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Article2 crawl(String url) { |
||||
|
return new Article2("测试标题", url, "测试内容", "作者", "2025-01-01"); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
factory.registerStrategy(blogStrategy); |
||||
|
|
||||
|
AnalyzeCommand2 analyzeCmd = new AnalyzeCommand2(factory); |
||||
|
HistoryCommand2 historyCmd = new HistoryCommand2(); |
||||
|
List<Article2> articles = new ArrayList<>(); |
||||
|
|
||||
|
Scanner sc = new Scanner(System.in); |
||||
|
System.out.println("===== 程序启动 ====="); |
||||
|
System.out.println("命令:"); |
||||
|
System.out.println("analyze 网址"); |
||||
|
System.out.println("history"); |
||||
|
System.out.println("exit\n"); |
||||
|
|
||||
|
while (true) { |
||||
|
System.out.print("请输入命令:"); |
||||
|
String input = sc.nextLine().trim(); |
||||
|
historyCmd.addCommand(input); |
||||
|
|
||||
|
if (input.equalsIgnoreCase("exit")) { |
||||
|
System.out.println("退出"); |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
if (input.startsWith("analyze ")) { |
||||
|
String[] arr = input.split(" ", 2); |
||||
|
analyzeCmd.execute(new String[]{arr[1]}, articles); |
||||
|
} else if (input.equalsIgnoreCase("history")) { |
||||
|
historyCmd.execute(new String[0], articles); |
||||
|
} else { |
||||
|
System.out.println("未知命令"); |
||||
|
} |
||||
|
} |
||||
|
sc.close(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
public class StrategyFactory2 { |
||||
|
private final List<CrawlStrategy2> strategies = new ArrayList<>(); |
||||
|
|
||||
|
public void registerStrategy(CrawlStrategy2 strategy) { |
||||
|
strategies.add(strategy); |
||||
|
} |
||||
|
|
||||
|
public CrawlStrategy2 getMatchStrategy(String url) { |
||||
|
for (CrawlStrategy2 s : strategies) { |
||||
|
if (s.supports(url)) return s; |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
After Width: | Height: | Size: 91 KiB |
Loading…
Reference in new issue