Browse Source

设计模式:灵活性与可扩展性

main
ZhangYuhan 3 weeks ago
parent
commit
a8e08f5d68
  1. 18
      w10/AI架构审计
  2. 34
      w10/AnalyzeCommand2.java
  3. 27
      w10/Article2.java
  4. 32
      w10/ArticleRepository.java
  5. 6
      w10/Command2.java
  6. 4
      w10/CrawlStrategy2.java
  7. 27
      w10/HistoryCommand2.java
  8. 56
      w10/Main3.java
  9. 17
      w10/StrategyFactory2.java
  10. BIN
      w10/类依赖图.png

18
w10/AI架构审计

@ -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将策略的注册、匹配逻辑封装为独立组件,调用方仅依赖工厂接口,无需关心具体实现;新增策略只需注册到工厂,不影响其他代码,既降低耦合,又提升了扩展性和可维护性,更符合面向对象设计规范。

34
w10/AnalyzeCommand2.java

@ -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("状态:不支持 ❌");
}
}
}

27
w10/Article2.java

@ -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; }
}

32
w10/ArticleRepository.java

@ -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();
}
}

6
w10/Command2.java

@ -0,0 +1,6 @@
import java.util.List;
public interface Command2 {
String getName();
void execute(String[] args, List<Article2> articles);
}

4
w10/CrawlStrategy2.java

@ -0,0 +1,4 @@
public interface CrawlStrategy2 {
boolean supports(String url);
Article2 crawl(String url);
}

27
w10/HistoryCommand2.java

@ -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);
}
}

56
w10/Main3.java

@ -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();
}
}

17
w10/StrategyFactory2.java

@ -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;
}
}

BIN
w10/类依赖图.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Loading…
Cancel
Save