1. 项目结构
- 项目结构
bash
observer-pattern-demo/
├── pom.xml
├── src/
│ └── main/
│ └── java/
│ └── com/
│ └── example/
│ ├── observer/
│ │ ├── NewsPublisher.java
│ │ ├── NewsSubscriber.java
│ │ ├── Subscriber.java
│ │ └── NewsType.java
│ └── Main.java
-
Maven 配置文件 (pom.xml)
xml<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>observer-pattern-demo</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>Observer Pattern Demo</name> <description>Java Observer Pattern Implementation with Maven</description> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project>
2. 代码实现
-
新闻类型枚举(NewsType.java)
javapackage com.example.observer; /** * 新闻类型枚举 */ public enum NewsType { POLITICS("ZZ新闻"), SPORTS("TY新闻"), TECHNOLOGY("KJ新闻"), ENTERTAINMENT("YL新闻"), BUSINESS("CJ新闻"); private final String description; NewsType(String description) { this.description = description; } public String getDescription() { return description; } } -
订阅者接口 (Subscriber.java)
javapackage com.example.observer; /** * 订阅者接口(观察者接口) * 所有观察者都需要实现这个接口 */ public interface Subscriber { /** * 更新方法,当主题状态变化时被调用 * @param newsType 新闻类型 * @param newsContent 新闻内容 */ void update(NewsType newsType, String newsContent); /** * 获取订阅者名称 * @return 订阅者名称 */ String getName(); } -
XW订阅者实现 (NewsSubscriber.java)
javapackage com.example.observer; import java.util.ArrayList; import java.util.List; /** * 具体的XW订阅者实现 */ public class NewsSubscriber implements Subscriber { private final String name; private final List<NewsType> subscribedTypes; private final List<String> receivedNews; public NewsSubscriber(String name) { this.name = name; this.subscribedTypes = new ArrayList<>(); this.receivedNews = new ArrayList<>(); } /** * 订阅特定类型的新闻 * @param newsType XW类型 */ public void subscribeTo(NewsType newsType) { if (!subscribedTypes.contains(newsType)) { subscribedTypes.add(newsType); System.out.println(name + " 订阅了 " + newsType.getDescription()); } } /** * 取消订阅特定类型的XW * @param newsType XW类型 */ public void unsubscribeFrom(NewsType newsType) { if (subscribedTypes.contains(newsType)) { subscribedTypes.remove(newsType); System.out.println(name + " 取消订阅 " + newsType.getDescription()); } } /** * 检查是否订阅了某种类型的XW * @param newsType XW类型 * @return 如果已订阅返回true,否则返回false */ public boolean isSubscribedTo(NewsType newsType) { return subscribedTypes.contains(newsType); } @Override public void update(NewsType newsType, String newsContent) { // 只有订阅了该类型XW的订阅者才会收到通知 if (subscribedTypes.contains(newsType)) { String newsMessage = String.format("[%s] %s: %s", newsType.getDescription(), name, newsContent); receivedNews.add(newsMessage); System.out.println(newsMessage); } } @Override public String getName() { return name; } /** * 获取所有收到的XW * @return 收到的XW列表 */ public List<String> getReceivedNews() { return new ArrayList<>(receivedNews); } /** * 显示所有收到的XW */ public void displayReceivedNews() { System.out.println("\n=== " + name + " 收到的XW ==="); if (receivedNews.isEmpty()) { System.out.println("暂无XW"); } else { for (int i = 0; i < receivedNews.size(); i++) { System.out.println((i + 1) + ". " + receivedNews.get(i)); } } System.out.println(); } } -
XW发布者 (NewsPublisher.java)
javapackage com.example.observer; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * XW发布者(主题/被观察者) * 负责管理订阅者和发布XW */ public class NewsPublisher { private final Map<NewsType, List<Subscriber>> subscribersByType; private final String publisherName; public NewsPublisher(String publisherName) { this.publisherName = publisherName; this.subscribersByType = new HashMap<>(); // 初始化所有XW类型的订阅者列表 for (NewsType type : NewsType.values()) { subscribersByType.put(type, new ArrayList<>()); } } /** * 订阅XW * @param subscriber 订阅者 * @param newsType XW类型 */ public void subscribe(Subscriber subscriber, NewsType newsType) { List<Subscriber> subscribers = subscribersByType.get(newsType); if (!subscribers.contains(subscriber)) { subscribers.add(subscriber); System.out.println(subscriber.getName() + " 成功订阅 " + publisherName + " 的 " + newsType.getDescription()); } } /** * 取消订阅 * @param subscriber 订阅者 * @param newsType XW类型 */ public void unsubscribe(Subscriber subscriber, NewsType newsType) { List<Subscriber> subscribers = subscribersByType.get(newsType); if (subscribers.contains(subscriber)) { subscribers.remove(subscriber); System.out.println(subscriber.getName() + " 已取消订阅 " + publisherName + " 的 " + newsType.getDescription()); } } /** * 发布XW * @param newsType XW类型 * @param newsContent XW内容 */ public void publishNews(NewsType newsType, String newsContent) { System.out.println("\n=== " + publisherName + " 发布XW ==="); System.out.println("【" + newsType.getDescription() + "】" + newsContent); System.out.println("通知订阅者..."); List<Subscriber> subscribers = subscribersByType.get(newsType); if (subscribers.isEmpty()) { System.out.println("当前没有订阅者订阅此类XW"); } else { // 通知所有订阅了该类型XW的订阅者 for (Subscriber subscriber : subscribers) { subscriber.update(newsType, newsContent); } System.out.println("已通知 " + subscribers.size() + " 位订阅者"); } } /** * 获取特定XW类型的订阅者数量 * @param newsType XW类型 * @return 订阅者数量 */ public int getSubscriberCount(NewsType newsType) { return subscribersByType.get(newsType).size(); } /** * 显示所有订阅者统计 */ public void displaySubscriptionStats() { System.out.println("\n=== " + publisherName + " 订阅统计 ==="); for (NewsType type : NewsType.values()) { int count = getSubscriberCount(type); System.out.println(type.getDescription() + ": " + count + " 位订阅者"); } } } -
主程序 (Main.java)
javapackage com.example; import com.example.observer.NewsPublisher; import com.example.observer.NewsSubscriber; import com.example.observer.NewsType; import com.example.observer.Subscriber; /** * 观察者模式演示主程序 */ public class Main { public static void main(String[] args) { System.out.println("=== 观察者模式演示:XW发布系统 ===\n"); // 创建XW发布者 NewsPublisher newsAgency = new NewsPublisher("HQ新闻社"); // 创建XW订阅者 Subscriber alice = new NewsSubscriber("Alice"); Subscriber bob = new NewsSubscriber("Bob"); Subscriber charlie = new NewsSubscriber("Charlie"); Subscriber diana = new NewsSubscriber("Diana"); // 订阅者选择感兴趣的XW类型 ((NewsSubscriber) alice).subscribeTo(NewsType.POLITICS); ((NewsSubscriber) alice).subscribeTo(NewsType.TECHNOLOGY); ((NewsSubscriber) bob).subscribeTo(NewsType.SPORTS); ((NewsSubscriber) bob).subscribeTo(NewsType.ENTERTAINMENT); ((NewsSubscriber) charlie).subscribeTo(NewsType.TECHNOLOGY); ((NewsSubscriber) charlie).subscribeTo(NewsType.BUSINESS); ((NewsSubscriber) diana).subscribeTo(NewsType.POLITICS); ((NewsSubscriber) diana).subscribeTo(NewsType.ENTERTAINMENT); ((NewsSubscriber) diana).subscribeTo(NewsType.BUSINESS); System.out.println("\n=== 订阅关系建立 ===\n"); // 订阅者向发布者注册 newsAgency.subscribe(alice, NewsType.POLITICS); newsAgency.subscribe(alice, NewsType.TECHNOLOGY); newsAgency.subscribe(bob, NewsType.SPORTS); newsAgency.subscribe(bob, NewsType.ENTERTAINMENT); newsAgency.subscribe(charlie, NewsType.TECHNOLOGY); newsAgency.subscribe(charlie, NewsType.BUSINESS); newsAgency.subscribe(diana, NewsType.POLITICS); newsAgency.subscribe(diana, NewsType.ENTERTAINMENT); newsAgency.subscribe(diana, NewsType.BUSINESS); // 显示订阅统计 newsAgency.displaySubscriptionStats(); System.out.println("\n=== 开始发布XW ===\n"); // 发布新闻 newsAgency.publishNews(NewsType.SPORTS, "中国女排获得世界冠军"); newsAgency.publishNews(NewsType.TECHNOLOGY, "人工智能取得重大突破"); newsAgency.publishNews(NewsType.ENTERTAINMENT, "奥斯卡颁奖典礼即将举行"); newsAgency.publishNews(NewsType.BUSINESS, "股市今日大幅上涨"); // 显示每个订阅者收到的XW ((NewsSubscriber) alice).displayReceivedNews(); ((NewsSubscriber) bob).displayReceivedNews(); ((NewsSubscriber) charlie).displayReceivedNews(); ((NewsSubscriber) diana).displayReceivedNews(); // 演示取消订阅 System.out.println("\n=== 演示取消订阅 ===\n"); newsAgency.unsubscribe(alice, NewsType.POLITICS); ((NewsSubscriber) alice).unsubscribeFrom(NewsType.POLITICS); newsAgency.displaySubscriptionStats(); // 再次发布XW,Alice将不会收到ZZ新闻 System.out.println("\n=== 再次发布ZZ新闻 ===\n"); newsAgency.publishNews(NewsType.POLITICS, "新外交政策发布"); // 显示Alice收到的ZW ((NewsSubscriber) alice).displayReceivedNews(); // 演示使用Java内置的Observer接口(可选) System.out.println("\n=== 使用Java内置Observer接口示例 ==="); demonstrateJavaBuiltInObserver(); } /** * 演示使用Java内置的Observer/Observable类 * 注意:从Java 9开始,Observable类被标记为过时(deprecated) * 这里仅作为示例展示 */ private static void demonstrateJavaBuiltInObserver() { System.out.println("注意:Java内置的java.util.Observable和Observer"); System.out.println("从Java 9开始已被标记为过时(deprecated)。"); System.out.println("推荐使用PropertyChangeListener或自定义观察者模式实现。"); } }
3. 构建和运行
-
编译项目
bash# 编译项目 mvn compile -
打包项目
bashmvn package -
运行项目
bashmvn exec:java -Dexec.mainClass="com.example.Main" -
直接运行
bash# 编译 javac -d target/classes src/main/java/com/example/observer/*.java src/main/java/com/example/Main.java # 运行 java -cp target/classes com.example.Main
4. 核心概念
- 松耦合:发布者和订阅者之间是松耦合的,可以独立修改
- 一对多关系:一个发布者可以对应多个订阅者
- 自动通知:当主题状态改变时,所有观察者会自动收到通知
- 动态订阅:订阅者可以随时订阅或取消订阅
额外扩展
- 添加过滤器:允许订阅者设置过滤器,只接收符合特定条件的新闻
- 优先级系统:为订阅者设置优先级,高优先级订阅者先收到通知
- 异步通知:使用线程池异步通知订阅者,提高系统响应速度
- 持久化订阅:将订阅关系保存到数据库,重启后可以恢复
- 推拉模式:实现推模式(主动推送)和拉模式(订阅者主动拉取)两种方式