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