当前位置 : 主页 > 编程语言 > java >

JAVA设计模式之观察者模式

来源:互联网 收集:自由互联 发布时间:2023-09-03
观察者模式是一种行为设计模式,允许对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都会得到通知并自动更新。在这种模式中,发生状态改变的对象

观察者模式是一种行为设计模式,允许对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都会得到通知并自动更新。在这种模式中,发生状态改变的对象被称为“主题”(Subject),依赖它的对象被称为“观察者”(Observer)。

观察者模式(Observer Design Pattern)也被称为发布订阅模式(Publish-Subscribe Design Pattern)。

一般情况下,被依赖的对象叫作被观察者(Observable),依赖的对象叫作观察者(Observer)。不过,在实际的项目开发中,这两种对象的称呼是比较灵活的,有各种不同的叫法,比如:Subject-Observer、Publisher-Subscriber、Producer-Consumer等等。不管怎么称呼,只要应用场景符合刚刚给出的定义,都可以看作观察者模式。

通过一个简单的例子来实现观察者模式。假设有一个气象站(WeatherStation),需要向许多不同的显示设备(如手机App、网站、电子屏幕等)提供实时天气数据。

JAVA设计模式之观察者模式_User

首先需要创建一个Subject接口,表示主题:

public interface Subject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}

创建一个Observer接口,表示观察者:

public interface Observer {
    void update(float temperature, float humidity, float pressure);
}

创建一个具体的主题,如WeatherStation,实现Subject接口:

public class WeatherStation implements Subject {
    private ArrayList<Observer> observers;
    // 温度
    private float temperature;
    // 湿度
    private float humidity;
    // 大气压
    private float pressure;

    public WeatherStation() {
        observers = new ArrayList<>();
    }

    // 注册一个观察者的方法
    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }

    // 移除一个观察者的方法
    @Override
    public void removeObserver(Observer o) {
        int index = observers.indexOf(o);
        if (index >= 0) {
            observers.remove(index);
        }
    }

    // 通知所有的观察者
    @Override
    public void notifyObservers() {
        // 循环所有的观察者,通知其当前的气象信息
        for (Observer o : observers) {
            o.update(temperature, humidity, pressure);
        }
    }

    // 修改气象内容
    public void measurementsChanged() {
        notifyObservers();
    }

    // 当测量值发生了变化的时候
    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        // 测量值发生了变化
        measurementsChanged();
    }
}

创建一个具体的观察者,如PhoneApp,实现Observer接口:

public class PhoneApp implements Observer {
    private float temperature;
    private float humidity;
    private float pressure;
    private Subject weatherStation;

    public PhoneApp(Subject weatherStation) {
        this.weatherStation = weatherStation;
        weatherStation.registerObserver(this);
    }

    @Override
    public void update(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        display();
    }

    public void display() {
        System.out.println("PhoneApp: Temperature: " + temperature + "°C, Humidity: " + humidity + "%, Pressure: " + pressure + " hPa");
    }
}

创建一个WeatherStation实例并向其注册PhoneApp观察者。当WeatherStation的数据发生变化时,PhoneApp会收到通知并更新自己的显示。

public class Main {
    public static void main(String[] args) {
        WeatherStation weatherStation = new WeatherStation();
        PhoneApp phoneApp = new PhoneApp(weatherStation);
        // 模拟气象站数据更新
        weatherStation.setMeasurements(25, 65, 1010);
        weatherStation.setMeasurements(22, 58, 1005);

        // 添加更多观察者  网站上显示-电子大屏
        WebsiteDisplay websiteDisplay = new WebsiteDisplay(weatherStation);
        ElectronicScreen electronicScreen = new ElectronicScreen(weatherStation);

        // 再次模拟气象站数据更新
        weatherStation.setMeasurements(18, 52, 1008);
    }
}

创建了一个WeatherStation实例,并向其注册了PhoneApp、WebsiteDisplay和ElectronicScreen观察者。当WeatherStation的数据发生变化时,所有观察者都会收到通知并更新自己的显示。 这个例子展示了观察者模式的优点:

  1. 观察者和主题之间的解耦:主题只需要知道观察者实现了Observer接口,而无需了解具体的实现细节。
  2. 可以动态添加和删除观察者:通过调用registerObserver和removeObserver方法,可以在运行时添加和删除观察者。
  3. 主题和观察者之间的通信是自动的:当主题的状态发生变化时,观察者会自动得到通知并更新自己的状态。 观察者模式广泛应用于各种场景,例如事件处理系统、数据同步和更新通知等。


使用场景

以下是一些使用观察者设计模式的例子:

  1. 网络聊天室:在网络聊天室中,当有新消息时,需要通知所有在线的用户。聊天室服务器可以作为被观察者,用户可以作为观察者。当有新消息时,聊天室服务器会通知所有在线用户更新聊天记录。
  2. 拍卖系统:在拍卖系统中,当出价发生变化时,需要通知所有关注该拍品的用户。这里,拍卖系统可以作为被观察者,用户可以作为观察者。当出价发生变化时,所有关注该拍品的用户都会收到通知并更新自己的出价策略。
  3. 订阅系统:在订阅系统中,当有新的内容发布时,需要通知所有订阅了该内容的用户。这里,内容发布可以作为被观察者,用户可以作为观察者。当有新内容发布时,所有订阅了该内容的用户都会收到通知并获取最新内容。
  4. 游戏中的事件系统:在游戏中,当某个事件发生时(如角色升级、道具获得等),可能需要通知多个游戏模块进行相应的处理。这里,游戏事件可以作为被观察者,游戏模块可以作为观察者。当游戏事件发生时,所有关注该事件的游戏模块都会收到通知并执行相应的逻辑。
  5. 运动比赛实时更新:在体育比赛中,实时更新比分、技术统计等信息对于球迷和分析师非常重要。在这种场景下,比赛数据更新可以作为被观察者,球迷和分析师可以作为观察者。当比赛数据发生变化时,所有关注比赛的球迷和分析师都会收到通知并更新数据。
  6. 物联网传感器系统:在物联网(IoT)系统中,有很多传感器不断地采集数据,当数据发生变化时,需要通知相关联的设备或系统。在这种场景下,传感器可以作为被观察者,关联的设备或系统可以作为观察者。当传感器数据发生变化时,所有关联的设备或系统都会收到通知并执行相应的操作。
  7. 电子邮件通知系统:在一个任务管理系统中,当任务的状态发生变化(如:新任务分配、任务完成等)时,需要通知相关的人员。这里,任务状态更新可以作为被观察者,相关人员可以作为观察者。当任务状态发生变化时,所有关注该任务的人员都会收到通知并查看任务详情。
  8. 社交网络:在社交网络中,用户关注其他用户以获取实时动态。当被关注的用户发布新动态时,需要通知所有关注者。在这种场景下,被关注的用户可以作为被观察者,关注者可以作为观察者。当被关注的用户发布新动态时,所有关注者都会收到通知并查看动态。

电商系统的应用

在电商系统中,观察者模式可以应用于多种场景,如库存管理、促销通知等。以下是一个促销通知的例子:

有一个电商系统,当某件商品有促销活动时,需要通知所有订阅了该商品的用户。在这个例子中,商品是主题,用户是观察者,其代码逻辑和第一节的例子不能说完全一样,也基本是一模一样。

创建一个Subject接口,表示主题:

public interface Subject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}

创建一个Observer接口,表示观察者:

public interface Observer {
    void update(String discountInfo);
}

创建一个具体的主题,如Product,实现Subject接口:

public class Product implements Subject {
    private ArrayList<Observer> observers;
    // 折扣消息
    private String discountInfo;

    public Product() {
        observers = new ArrayList<>();
    }

    public void registerObserver(Observer o) {
        observers.add(o);
    }

    public void removeObserver(Observer o) {
        int index = observers.indexOf(o);
        if (index >= 0) {
            observers.remove(index);
        }
    }

    public void notifyObservers() {
        for (Observer o : observers) {
            o.update(discountInfo);
        }
    }

    public void discountChanged() {
        notifyObservers();
    }

    public void setDiscountInfo(String discountInfo) {
        this.discountInfo = discountInfo;
        discountChanged();
    }
}

创建一个具体的观察者,如User,实现Observer接口:

public class User implements Observer {
    private String userName;
    private String discountInfo;
    private Subject product;

    public User(String userName, Subject product) {
        this.userName = userName;
        this.product = product;
        product.registerObserver(this);
    }

    public void update(String discountInfo) {
        this.discountInfo = discountInfo;
        display();
    }

    public void display() {
        System.out.println("用户 " + userName + " 收到促销通知: " + discountInfo);
    }
}

创建一个Product实例并向其注册User观察者。当Product的促销信息发生变化时,User会收到通知并显示促销信息。

public class Main {
    public static void main(String[] args) {
        Product product = new Product();
        User user1 = new User("张三", product);
        User user2 = new User("李四", product);

        // 模拟商品促销信息更新
        product.setDiscountInfo("本周末满100减50");
        product.setDiscountInfo("双十一全场5折");
    }
}

创建了一个Product实例并向其注册了两个User观察者。当Product的促销信息发生变化时,所有观察者都会收到通知并更新自己的显示。

erp

在ERP系统中,观察者模式也有很多应用场景,例如库存管理、生产计划等。一个库存管理的例子:

假设有一个ERP系统,当某个产品的库存低于安全库存时,需要通知采购部门、销售部门和仓库管理员。在这个例子中,产品库存是主题,采购部门、销售部门和仓库管理员是观察者。

创建一个具体的主题,如Inventory,实现Subject接口:

public class Inventory implements Subject {
    private ArrayList<Observer> observers;
    private int stock;

    public Inventory() {
        observers = new ArrayList<>();
    }

    public void registerObserver(Observer o) {
        observers.add(o);
    }

    public void removeObserver(Observer o) {
        int index = observers.indexOf(o);
        if (index >= 0) {
            observers.remove(index);
        }
    }

    public void notifyObservers() {
        for (Observer o : observers) {
            o.update(String.valueOf(stock));
        }
    }

    public void stockChanged() {
        notifyObservers();
    }

    public void setStock(int stock) {
        this.stock = stock;
        stockChanged();
    }
}

创建一个具体的观察者,如PurchaseDepartment、SalesDepartment和WarehouseManager,分别实现Observer接口:

public class PurchaseDepartment implements Observer {
    private int stock;
    private Subject inventory;

    public PurchaseDepartment(Subject inventory) {
        this.inventory = inventory;
        inventory.registerObserver(this);
    }

    public void update(String stock) {
        this.stock = Integer.parseInt(stock);
        display();
    }

    public void display() {
        System.out.println("采购部门收到库存更新: " + stock);
    }
}

public class SalesDepartment implements Observer {
    private int stock;
    private Subject inventory;

    public SalesDepartment(Subject inventory) {
        this.inventory = inventory;
        inventory.registerObserver(this);
    }

    public void update(String stock) {
        this.stock = Integer.parseInt(stock);
        display();
    }

    public void display() {
        System.out.println("销售部门收到库存更新: " + stock);
    }
}

public class WarehouseManager implements Observer {
    private int stock;
    private Subject inventory;

    public WarehouseManager(Subject inventory) {
        this.inventory = inventory;
        inventory.registerObserver(this);
    }

    public void update(String stock) {
        this.stock = Integer.parseInt(stock);
        display();
    }

    public void display() {
        System.out.println("仓库管理员收到库存更新: " + stock);
    }
}

创建一个Inventory实例并向其注册PurchaseDepartment、SalesDepartment和WarehouseManager观察者。当Inventory的库存发生变化时,所有观察者会收到通知并更新自己的显示。

public class Main {
    public static void main(String[] args) {
        Inventory inventory = new Inventory();
        PurchaseDepartment purchaseDepartment = new PurchaseDepartment(inventory);
        SalesDepartment salesDepartment = new SalesDepartment(inventory);
        WarehouseManager warehouseManager = new WarehouseManager(inventory);

        // 模拟库存变化
        inventory.setStock(500);
        inventory.setStock(300);
        inventory.setStock(100);
    }
}

创建了一个Inventory实例并向其注册了PurchaseDepartment、SalesDepartment和WarehouseManager观察者。当Inventory的库存发生变化时,所有观察者都会收到通知并更新自己的显示。 这个例子展示了观察者模式在ERP系统中的一个应用场景,实现了库存信息与相关部门(采购部门、销售部门、仓库管理员)之间的交互。这种模式可以帮助企业在库存发生变化时快速作出相应的决策。


发布订阅

发布-订阅模式和观察者模式都是用于实现对象间的松耦合通信的设计模式。尽管它们具有相似之处,但它们在实现方式和使用场景上存在一些关键区别。他们在概念上有一定的相似性,都是用于实现对象间的松耦合通信。可以将发布-订阅模式看作是观察者模式的一种变体或扩展。

观察者模式

观察者模式定义了一种一对多的依赖关系,当一个对象(被观察者)的状态发生变化时,所有依赖于它的对象(观察者)都会得到通知并自动更新。在这个模式中,被观察者和观察者之间存在直接的关联关系。观察者模式主要包括两类对象:被观察者(Subject)和观察者(Observer)。

Java中的观察者模式示例:

interface Observer {
    void update(String message);
}

class ConcreteObserver implements Observer {
    @Override
    public void update(String message) {
        System.out.println("收到通知: " + message);
    }
}

interface Subject {
    void addObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}

class ConcreteSubject implements Subject {
    private List<Observer> observers = new ArrayList<>();

    @Override
    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update("状态发生变化");
        }
    }
}

发布-订阅模式

发布-订阅模式(生产者和消费者)与观察者模式类似,但它们之间有一个关键区别:发布-订阅模式引入了一个第三方组件(通常称为消息代理或事件总线),该组件负责维护发布者和订阅者之间的关系。这意味着发布者和订阅者彼此不直接通信,而是通过消息代理进行通信。这种间接通信允许发布者和订阅者在运行时动态地添加或删除,从而提高了系统的灵活性和可扩展性。

Java中的发布-订阅模式示例:

interface Subscriber {
    void onEvent(String event);
}

class ConcreteSubscriber implements Subscriber {
    @Override
    public void onEvent(String event) {
        System.out.println("收到事件: " + event);
    }
}

// 创建消息总线
class EventBus {
    // 使用一个map维护,消息类型和该消息的订阅者
    private Map<String, List<Subscriber>> subscribers = new HashMap<>();

    // 订阅一个消息
    public void subscribe(String eventType, Subscriber subscriber) {
        subscribers.computeIfAbsent(eventType, k -> new ArrayList<>()).add(subscriber);
    }

    // 接触订阅
    public void unsubscribe(String eventType, Subscriber subscriber) {
        List<Subscriber> subs = subscribers.get(eventType);
        if (subs != null) {
            subs.remove(subscriber);
        }
    }

    // 发布事件
    public void publish(String eventType, String event) {
        List<Subscriber> subs = subscribers.get(eventType);
        if (subs != null) {
            for (Subscriber subscriber : subs) {
                subscriber.onEvent(event);
            }
        }
    }
}

// 使用示例:
public class Main {
    public static void main(String[] args) {
        EventBus eventBus = new EventBus();
        Subscriber subscriber1 = new ConcreteSubscriber();
        Subscriber subscriber2 = new ConcreteSubscriber();
        // 订阅事件
        eventBus.subscribe("eventA", subscriber1);
        eventBus.subscribe("eventA", subscriber2);

        // 发布事件
        eventBus.publish("eventA", "这是事件A的内容");

        // 取消订阅
        eventBus.unsubscribe("eventA", subscriber1);

        // 再次发布事件
        eventBus.publish("eventA", "这是事件A的新内容");
    }
}

总结一下两者的区别:

  1. 通信方式:观察者模式中,观察者与被观察者之间存在直接的关联关系,而发布-订阅模式中,发布者和订阅者通过一个第三方组件(消息代理或事件总线)进行通信,彼此之间不存在直接关联关系。
  2. 系统复杂性:发布-订阅模式引入了一个额外的组件(消息代理或事件总线),增加了系统的复杂性,但同时也提高了系统的灵活性和可扩展性。
  3. 使用场景:观察者模式适用于需要将状态变化通知给其他对象的情况,而发布-订阅模式适用于事件驱动的系统,尤其是那些需要跨越多个模块或组件进行通信的场景。 希望这个解释能帮助您理解发布-订阅模式和观察者模式之间的区别。如果您有其他问题,欢迎继续提问。

发布-订阅模式和传统的观察者模式相比,在某些方面具有优势。以下是发布-订阅模式相对于观察者模式的一些优点:

  1. 解耦:在发布-订阅模式中,发布者和订阅者之间没有直接关联,它们通过一个中间组件(消息代理或事件总线)进行通信。这种间接通信可以使发布者和订阅者在运行时动态地添加或删除,从而进一步降低了它们之间的耦合度。
  2. 可扩展性:发布-订阅模式允许您更容易地向系统中添加新的发布者和订阅者,而无需修改现有的代码。这使得系统在不同组件之间通信时具有更好的可扩展性。
  3. 模块化:由于发布者和订阅者之间的通信通过中间组件进行,您可以将系统划分为更小、更独立的模块。这有助于提高代码的可维护性和可读性。
  4. 异步通信:发布-订阅模式通常支持异步消息传递,这意味着发布者和订阅者可以在不同的线程或进程中运行。这有助于提高系统的并发性能和响应能力。
  5. 消息过滤:在发布-订阅模式中,可以利用中间组件对消息进行过滤,使得订阅者只接收到感兴趣的消息。这可以提高系统的性能,减少不必要的通信开销。

然而,发布-订阅模式也有一些缺点,例如增加了系统的复杂性,因为引入了额外的中间组件。根据具体的应用场景和需求来选择合适的设计模式是很重要的。在某些情况下,观察者模式可能更适合,而在其他情况下,发布-订阅模式可能是更好的选择。

源码使用

jdk中的观察者

java.util.Observable类实现了主题(Subject)的功能,而java.util.Observer接口则定义了观察者(Observer)的方法。

通过调用Observable对象的notifyObservers()方法,可以通知所有注册的Observer对象,让它们更新自己的状态。

使用案例:假设有一个银行账户类,它的余额是可变的。当余额发生变化时,需要通知所有的观察者(比如说银行客户),以便它们更新自己的显示信息。

// 银行账户类
public class BankAccount extends Observable {
    private double balance;

    // 构造函数
    public BankAccount(double balance) {
        this.balance = balance;
    }

    // 存款操作
    public void deposit(double amount) {
        balance += amount;
        setChanged(); // 表示状态已经改变
        notifyObservers(); // 通知所有观察者
    }

    // 取款操作
    public void withdraw(double amount) {
        balance -= amount;
        setChanged(); // 表示状态已经改变
        notifyObservers(); // 通知所有观察者
    }

    // 获取当前余额
    public double getBalance() {
        return balance;
    }

    // 主函数
    public static void main(String[] args) {
        BankAccount account = new BankAccount(1000.0);
        // 创建观察者
        Observer observer1 = new Observer() {
            @Override
            public void update(Observable o, Object arg) {
                System.out.println("客户1: 余额已更新为 " + ((BankAccount)o).getBalance());
            }
        };
        Observer observer2 = new Observer() {
            @Override
            public void update(Observable o, Object arg) {
                System.out.println("客户2: 余额已更新为 " + ((BankAccount)o).getBalance());
            }
        };
        // 注册观察者
        account.addObserver(observer1);
        account.addObserver(observer2);
        // 存款操作,触发观察者更新
        account.deposit(100.0);
        // 取款操作,触发观察者更新
        account.withdraw(50.0);
    }
}

这个案例中,BankAccount类继承了java.util.Observable类,表示它是一个主题(Subject)。在存款或取款操作时,它会调用setChanged()方法表示状态已经改变,并调用notifyObservers()方法通知所有观察者(Observer)。

在主函数中,创建了两个观察者(observer1和observer2),它们分别实现了Observer接口的update()方法。当观察者收到更新通知时,它们会执行自己的业务逻辑,比如更新显示信息。

这个案例演示了观察者模式在银行系统中的应用,通过观察者模式可以实现银行客户对自己账户余额的实时监控。

Guava中的消息总线

Guava 库中的 EventBus 类提供了一个简单的消息总线实现,可以帮助您在 Java 应用程序中实现发布-订阅模式。以下是一个简单的示例,演示了如何使用 Guava 的 EventBus 来实现一个简单的消息发布和订阅功能。

首先,确保已将 Guava 添加到项目的依赖项中。如果您使用 Maven,请在 pom.xml 文件中添加以下依赖项:

<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>30.1-jre</version>
</dependency>

定义一个事件类,例如 MessageEvent

public class MessageEvent {
    private String message;

    public MessageEvent(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

现在,创建一个订阅者类,例如 MessageSubscriber。在订阅者类中,定义一个方法并使用 @Subscribe 注解标记该方法,以便 EventBus 能够识别该方法作为事件处理器:

public class MessageSubscriber {
    @Subscribe
    public void handleMessageEvent(MessageEvent event) {
        System.out.println("收到消息: " + event.getMessage());
    }
}

使用示例:

public class Main {
    public static void main(String[] args) {
        // 创建 EventBus 实例
        EventBus eventBus = new EventBus();

        // 创建并注册订阅者
        MessageSubscriber subscriber = new MessageSubscriber();
        eventBus.register(subscriber);

        // 发布事件
        eventBus.post(new MessageEvent("Hello, EventBus!"));

        // 取消注册订阅者
        eventBus.unregister(subscriber);

        // 再次发布事件(此时订阅者已取消注册,将不会收到消息)
        eventBus.post(new MessageEvent("Another message"));
    }
}

创建了一个 EventBus 实例,然后创建并注册了一个 MessageSubscriber 类型的订阅者。当使用 eventBus.post() 方法发布一个 MessageEvent 事件时,订阅者的 handleMessageEvent 方法将被调用,并输出收到的消息。

注意,如果订阅者处理事件的方法抛出异常,EventBus 默认情况下不会对异常进行处理。如果需要处理异常,可以在创建 EventBus 实例时传入一个自定义的 SubscriberExceptionHandler

进阶

观察者模式的应用场景非常广泛,小到代码层面的解耦,大到架构层面的系统解耦,再或者一些产品的设计思路,都有这种模式的影子。

不同的应用场景和需求下,这个模式也有截然不同的实现方式,之前所列举的所有的例子都是同步阻塞的实现方式,观察者设计模式也有异步非阻塞的实现方式;有进程内的实现方式,也有跨进程的实现方式。

之前讲到的实现方式,是一种同步阻塞的实现方式。观察者和被观察者代码在同一个线程内执行,被观察者一直阻塞,直到所有的观察者代码都执行完成之后,才执行后续的代码。对照上面讲到的用户注册的例子,register() 函数依次调用执行每个观察者的 handleRegSuccess() 函数,等到都执行完成之后,才会返回结果给客户端。

如果注册接口是一个调用比较频繁的接口,对性能非常敏感,希望接口的响应时间尽可能短,可以将同步阻塞的实现方式改为异步非阻塞的实现方式,以此来减少响应时间。

异步非阻塞模型

需要创建一个通用的观察者接口Observer和一个被观察者接口Observable

Observer.java:

public interface Observer {
    void update(String message);
}

Observable.java:

public interface Observable {
    void addObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers(String message);
}

需要实现一个具体的被观察者类Subject和一个具体的观察者类ConcreteObserver

Subject.java:

public class Subject implements Observable {
    private List<Observer> observers;
    private ExecutorService executorService;

    public Subject() {
        observers = new ArrayList<>();
        executorService = Executors.newCachedThreadPool();
    }

    @Override
    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers(String message) {
        for (Observer observer : observers) {
            executorService.submit(() -> observer.update(message));
        }
    }

    public void setMessage(String message) {
        notifyObservers(message);
    }
}

ConcreteObserver.java:

public class ConcreteObserver implements Observer {
    private String name;

    public ConcreteObserver(String name) {
        this.name = name;
    }

    @Override
    public void update(String message) {
        System.out.println(name + " received message: " + message);
    }
}

创建一个简单的示例来测试实现的异步非阻塞观察者模式。

Main.java:

public class Main {
    public static void main(String[] args) {
        Subject subject = new Subject();
        ConcreteObserver observer1 = new ConcreteObserver("Observer 1");
        ConcreteObserver observer2 = new ConcreteObserver("Observer 2");
        ConcreteObserver observer3 = new ConcreteObserver("Observer 3");

        subject.addObserver(observer1);
        subject.addObserver(observer2);
        subject.addObserver(observer3);

        subject.setMessage("Hello, observers!");

        // 等待异步任务完成
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

使用了ExecutorService的线程池来实现异步非阻塞的通知。每个观察者更新操作都将作为一个任务提交给线程池并异步执行。这将确保性能敏感的场景不会因为观察者的通知而阻塞。

跨进程通信

刚刚讲到的两个场景,不管是同步阻塞实现方式还是异步非阻塞实现方式,都是进程内的实现方式。如果用户注册成功之后,需要发送用户信息给大数据征信系统,而大数据征信系统是一个独立的系统,跟它之间的交互是跨不同进程的,那如何实现一个跨进程的观察者模式呢?

如果大数据征信系统提供了发送用户注册信息的 RPC 接口,仍然可以沿用之前的实现思路,在 notifyObservers() 函数中调用 RPC 接口来发送数据。还有更加优雅、更加常用的一种实现方式,那就是基于消息队列(Message Queue,比如 ActiveMQ)来实现。

当然,这种实现方式也有弊端,那就是需要引入一个新的系统(消息队列),增加了维护成本。不过,它的好处也非常明显。在原来的实现方式中,观察者需要注册到被观察者中,被观察者需要依次遍历观察者来发送消息。而基于消息队列的实现方式,被观察者和观察者解耦更加彻底,两部分的耦合更小。被观察者完全不感知观察者,同理,观察者也完全不感知被观察者。被观察者只管发送消息到消息队列,观察者只管从消息队列中读取消息来执行相应的逻辑。

【文章原创作者:大丰网页设计 http://www.1234xp.com/dafeng.html 复制请保留原URL】
上一篇:JAVA设计模式之模板模式
下一篇:没有了
网友评论