解锁观察者模式:Java编程中的高效事件管理之道
系列文章目录
后续补充~~~
文章目录
- 一、引言:探索观察者模式的奥秘
- 二、观察者模式的核心原理
- 2.1 模式定义与概念
- 2.2 关键角色剖析
- 2.3 工作机制深度解析
- 三、观察者模式在 Java 中的实现
- 3.1 手动实现观察者模式
- 3.2 使用 JDK 内置的观察者模式
- 3.3 代码示例解析与对比
- 四、观察者模式的应用项目场景
- 4.1 电商系统中的应用
- 4.2 游戏开发中的应用
- 4.3 消息推送系统中的应用
- 五、观察者模式的优势与挑战
- 5.1 显著优势
- 5.2 面临的挑战
- 六、观察者模式与其他设计模式的关联
- 6.1 与 MVC 模式的关系
- 6.2 与发布 - 订阅模式的异同
- 七、总结与展望
- 7.1 回顾与总结
- 7.2 未来发展趋势
一、引言:探索观察者模式的奥秘
在软件开发的广袤宇宙中,设计模式犹如璀璨星辰,照亮了我们前行的道路。它们是前辈们智慧的结晶,是解决复杂问题的高效策略,是构建高质量软件系统的基石。设计模式能够帮助我们提升代码的可维护性、可扩展性和可复用性,让我们的软件更加健壮、灵活和易于理解。无论是小型项目还是大型企业级应用,设计模式都发挥着不可或缺的重要作用。
观察者模式,作为设计模式大家族中的一员,以其独特的魅力和广泛的应用场景,备受开发者们的青睐。它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式就像是一场精心编排的交响乐,主题对象如同指挥家,而观察者对象则是各司其职的演奏者,当指挥家发出信号时,演奏者们便会协同演奏出美妙的乐章。
在实际应用中,观察者模式有着丰富多样的体现。在图形用户界面(GUI)开发中,窗口的大小改变或按钮的点击等事件,都可以通过观察者模式来通知相关的组件进行相应的更新。在游戏开发中,角色的状态变化,如生命值、魔法值的改变,以及位置的移动等,都可以通过观察者模式来通知其他相关的游戏元素,如敌人、队友、场景等,从而实现更加丰富和真实的游戏体验。在消息队列系统中,生产者和消费者之间的关系也可以看作是观察者模式的应用,生产者发布消息,消费者作为观察者订阅消息并进行处理。
接下来,让我们深入探索观察者模式的核心概念、工作原理、实现方式以及在实际项目中的应用案例,揭开它神秘的面纱,领略它的独特魅力。
二、观察者模式的核心原理
2.1 模式定义与概念
观察者模式(Observer Pattern)定义了对象之间的一种一对多依赖关系,使得每当一个对象(被称为主题,Subject)的状态发生改变时,其所有依赖于它的对象(被称为观察者,Observer)都会得到通知并被自动更新。这种模式有时也被称为发布 - 订阅模式(Publish - Subscribe Pattern),主题相当于发布者,观察者相当于订阅者,发布者发布消息时,订阅者会收到通知并进行相应的处理。
观察者模式主要包含以下几个关键概念:
- 主题(Subject):也被称为被观察对象,它维护着一个观察者列表,当自身状态发生变化时,会通知列表中的所有观察者。主题提供了注册、注销观察者的方法,以及通知观察者的方法。
- 观察者(Observer):定义了一个更新接口,当接收到主题的通知时,会调用这个接口来更新自身的状态。不同的观察者可以根据自身的需求,实现不同的更新逻辑。
- 依赖关系:观察者与主题之间存在着依赖关系,观察者依赖于主题的状态变化,当主题状态改变时,观察者会得到通知并做出相应的反应。这种依赖关系是一对多的,即一个主题可以有多个观察者,而一个观察者只能依赖于一个主题。
2.2 关键角色剖析
在观察者模式中,通常包含以下四个关键角色:
-
抽象主题(Subject):抽象主题是一个抽象类或接口,它定义了观察者的注册、注销和通知方法。具体主题需要实现这些方法,以管理观察者列表并在状态改变时通知观察者。抽象主题一般包含以下几个要素:
- 一个用于存储观察者对象引用的集合,如 Java 中的List、Set等。
- attach(Observer observer)方法:用于将观察者添加到观察者列表中。
- detach(Observer observer)方法:用于从观察者列表中移除观察者。
- notifyObservers()方法:当主题状态发生变化时,调用此方法通知所有注册的观察者。
-
具体主题(Concrete Subject):具体主题是抽象主题的具体实现类,它维护着主题的具体状态,并在状态发生改变时,调用notifyObservers()方法通知所有观察者。具体主题通常包含以下几个要素:
- 存储主题的具体状态的成员变量。
- 实现抽象主题中定义的注册、注销和通知方法,在通知方法中,遍历观察者列表,调用每个观察者的更新方法。
-
抽象观察者(Observer):抽象观察者是一个抽象类或接口,它定义了一个更新方法update(),当观察者接收到主题的通知时,会调用这个方法来更新自身的状态。不同的具体观察者可以根据自身的需求,实现不同的更新逻辑。
-
具体观察者(Concrete Observer):具体观察者是抽象观察者的具体实现类,它实现了抽象观察者中定义的update()方法,在这个方法中,根据主题的状态变化,更新自身的状态或执行相应的操作。具体观察者通常包含以下几个要素:
- 存储观察者自身状态的成员变量,这些状态可能会根据主题的状态变化而更新。
- 实现update()方法,在方法中根据主题的状态变化,更新自身的状态或执行相应的操作。
这四个角色之间的相互关系如下:
- 抽象主题和具体主题之间是继承关系,具体主题继承抽象主题,并实现其抽象方法。
- 抽象观察者和具体观察者之间是继承关系,具体观察者继承抽象观察者,并实现其抽象方法。
- 具体主题和具体观察者之间是依赖关系,具体主题依赖于具体观察者,通过观察者列表来管理具体观察者,并在状态变化时通知它们。具体观察者依赖于具体主题,通过实现抽象观察者的更新方法,来响应具体主题的状态变化。
2.3 工作机制深度解析
为了更深入地理解观察者模式的工作机制,我们结合一个具体的例子来进行说明。假设我们正在开发一个简单的股票市场监测系统,其中股票价格作为主题,而投资者作为观察者。当股票价格发生变化时,所有关注该股票的投资者都应该得到通知并更新自己的投资策略。
注册观察者:投资者(具体观察者)通过调用股票(具体主题)的attach(Observer observer)方法,将自己注册到股票的观察者列表中。例如:
- 注册观察者:投资者(具体观察者)通过调用股票(具体主题)的attach(Observer observer)方法,将自己注册到股票的观察者列表中。例如:
Stock stock = new Stock();
Investor investor1 = new Investor("张三");
Investor investor2 = new Investor("李四");
stock.attach(investor1);
stock.attach(investor2);
- 状态改变:当股票价格发生变化时,股票(具体主题)会更新自己的状态,并调用notifyObservers()方法通知所有注册的投资者(具体观察者)。例如:
stock.setPrice(100.0); // 股票价格更新为100.0
在setPrice方法中,除了更新股票价格,还会调用notifyObservers方法:
public void setPrice(double price) {this.price = price;notifyObservers();
}
- 通知观察者:股票(具体主题)在notifyObservers()方法中,遍历观察者列表,调用每个投资者(具体观察者)的update()方法,将股票价格的变化通知给他们。例如:
public void notifyObservers() {for (Observer observer : observers) {observer.update(price);}
}
- 观察者更新:投资者(具体观察者)在接收到通知后,会根据股票价格的变化,更新自己的投资策略。例如:
public class Investor implements Observer {private String name;public Investor(String name) {this.name = name;}@Overridepublic void update(double price) {System.out.println(name + " 收到股票价格更新通知,当前价格为: " + price);// 根据股票价格更新投资策略,这里可以是具体的投资逻辑}
}
通过以上步骤,观察者模式实现了主题和观察者之间的解耦,使得当主题的状态发生变化时,所有依赖于它的观察者都能够得到通知并自动更新,从而实现了系统的灵活性和可扩展性。
三、观察者模式在 Java 中的实现
3.1 手动实现观察者模式
在 Java 中,我们可以通过手动编写代码来实现观察者模式。下面通过一个简单的示例来展示如何实现。
假设我们正在开发一个天气监测系统,其中天气数据作为主题,而不同的显示组件(如当前天气显示、天气预报显示等)作为观察者。当天气数据发生变化时,所有的显示组件都应该得到通知并更新显示。
首先,定义抽象主题接口Subject,它包含注册观察者、移除观察者和通知观察者的方法:
import java.util.ArrayList;
import java.util.List;public interface Subject {void registerObserver(Observer observer);void removeObserver(Observer observer);void notifyObservers();
}
然后,定义抽象观察者接口Observer,它包含一个更新方法,当接收到主题的通知时会调用这个方法:
public interface Observer {void update(float temperature, float humidity, float pressure);
}
接下来,创建具体主题类WeatherData,实现Subject接口,并包含天气数据和观察者列表:
public class WeatherData implements Subject {private List<Observer> observers;private float temperature;private float humidity;private float pressure;public WeatherData() {observers = new ArrayList<>();}@Overridepublic void registerObserver(Observer observer) {observers.add(observer);}@Overridepublic void removeObserver(Observer observer) {observers.remove(observer);}@Overridepublic void notifyObservers() {for (Observer observer : observers) {observer.update(temperature, humidity, pressure);}}public void setMeasurements(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;measurementsChanged();}private void measurementsChanged() {notifyObservers();}
}
再创建具体观察者类CurrentConditionsDisplay,实现Observer接口,并在更新方法中更新显示:
public class CurrentConditionsDisplay implements Observer {private float temperature;private float humidity;private Subject weatherData;public CurrentConditionsDisplay(Subject weatherData) {this.weatherData = weatherData;weatherData.registerObserver(this);}@Overridepublic void update(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;display();}public void display() {System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");}
}
最后,编写测试代码来验证观察者模式的实现:
public class WeatherStation {public static void main(String[] args) {WeatherData weatherData = new WeatherData();CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);weatherData.setMeasurements(80, 65, 30.4f);}
}
在上述代码中,WeatherData类作为具体主题,维护着一个观察者列表,并在天气数据更新时通知所有观察者。CurrentConditionsDisplay类作为具体观察者,实现了Observer接口的update方法,在接收到通知时更新自身的显示。
3.2 使用 JDK 内置的观察者模式
除了手动实现观察者模式,Java 的 JDK 中也提供了内置的观察者模式支持,通过java.util.Observable类和java.util.Observer接口来实现。
Observable类是被观察者的抽象类,它提供了添加、删除观察者以及通知观察者的方法。Observer接口则定义了观察者的更新方法。
下面是使用 JDK 内置观察者模式实现的天气监测系统示例:
首先,创建被观察者类WeatherData,继承自Observable:
import java.util.Observable;public class WeatherData extends Observable {private float temperature;private float humidity;private float pressure;public void setMeasurements(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;setChanged();notifyObservers();}public float getTemperature() {return temperature;}public float getHumidity() {return humidity;}public float getPressure() {return pressure;}
}
然后,创建观察者类CurrentConditionsDisplay,实现Observer接口:
import java.util.Observable;
import java.util.Observer;public class CurrentConditionsDisplay implements Observer {private float temperature;private float humidity;private Observable weatherData;public CurrentConditionsDisplay(Observable weatherData) {this.weatherData = weatherData;weatherData.addObserver(this);}@Overridepublic void update(Observable o, Object arg) {if (o instanceof WeatherData) {WeatherData weatherData = (WeatherData) o;this.temperature = weatherData.getTemperature();this.humidity = weatherData.getHumidity();display();}}public void display() {System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");}
}
最后,编写测试代码:
public class WeatherStation {public static void main(String[] args) {WeatherData weatherData = new WeatherData();CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);weatherData.setMeasurements(80, 65, 30.4f);}
}
在这个示例中,WeatherData类继承自Observable,通过调用setChanged()方法标记状态已改变,然后调用notifyObservers()方法通知所有观察者。CurrentConditionsDisplay类实现了Observer接口的update方法,在接收到通知时更新显示。
在 JDK 9 中,java.util.Observer 接口和 java.util.Observable 类被标记为已弃用。原因是有各种各样的缺陷,线程不安全等等。
3.3 代码示例解析与对比
手动实现观察者模式和使用 JDK 内置的观察者模式各有优缺点,适用于不同的场景。
手动实现观察者模式的优点:
- 灵活性高:可以根据具体需求自由定义主题和观察者的接口与实现,不受 JDK 内置类的限制。
- 理解性强:通过手动编写代码,能够更深入地理解观察者模式的工作原理和实现细节,有助于学习和掌握设计模式。
手动实现观察者模式的缺点:
- 代码量大:需要手动编写注册、移除观察者以及通知观察者的方法,代码相对繁琐。
- 维护成本高:如果对观察者模式的实现进行修改,可能需要在多个地方进行调整,增加了维护的难度。
使用 JDK 内置观察者模式的优点:
- 代码简洁:利用 JDK 提供的Observable类和Observer接口,减少了重复代码的编写,使代码更加简洁。
- 易于使用:JDK 内置的观察者模式已经经过了大量的测试和优化,使用起来更加稳定和可靠。
使用 JDK 内置观察者模式的缺点:
- 灵活性受限:Observable类是一个具体类,而不是接口,这限制了它的扩展性。如果需要继承其他类,就无法再继承Observable类。
- 线程安全问题:Observable类的实现不是线程安全的,在多线程环境下使用时需要额外的同步机制。
在实际应用中,如果对灵活性要求较高,或者需要深入理解观察者模式的实现原理,可以选择手动实现观察者模式。如果追求代码的简洁性和易用性,并且对扩展性要求不高,可以使用 JDK 内置的观察者模式。
四、观察者模式的应用项目场景
4.1 电商系统中的应用
在电商系统中,观察者模式有着广泛的应用,它能够有效地提升系统的灵活性和可扩展性,为用户提供更加优质的购物体验。下面我们将详细探讨观察者模式在电商系统中的两个重要应用场景:商品库存管理和价格变化通知。
商品库存管理:在电商系统中,商品库存是一个关键的因素。当商品库存数量发生变化时,需要及时通知相关的模块,如商品详情页、购物车、订单系统等,以便它们能够实时更新库存信息,避免出现超卖或库存显示不一致的情况。
假设我们有一个电商系统,其中包含商品类Product作为具体主题,以及商品详情页类ProductDetailPage和购物车类ShoppingCart作为具体观察者。当商品库存发生变化时,Product类会通知所有注册的观察者。
import java.util.ArrayList;
import java.util.List;// 观察者接口
interface Observer {void update(int stockQuantity);
}// 被观察者类(商品类)
class Product {private List<Observer> observers = new ArrayList<>();private int stockQuantity;public void addObserver(Observer observer) {observers.add(observer);}public void removeObserver(Observer observer) {observers.remove(observer);}private void notifyObservers() {for (Observer observer : observers) {observer.update(stockQuantity);}}public void setStockQuantity(int stockQuantity) {this.stockQuantity = stockQuantity;System.out.println("商品库存更新为: " + stockQuantity);notifyObservers();}
}// 商品详情页(观察者)
class ProductDetailPage implements Observer {@Overridepublic void update(int stockQuantity) {System.out.println("商品详情页更新库存显示为: " + stockQuantity);}
}// 购物车(观察者)
class ShoppingCart implements Observer {@Overridepublic void update(int stockQuantity) {System.out.println("购物车更新库存显示为: " + stockQuantity);}
}
在上述代码中,Product类维护了一个观察者列表,当库存数量发生变化时,会调用notifyObservers方法通知所有观察者。ProductDetailPage和ShoppingCart类实现了Observer接口,在update方法中更新各自的库存显示。
价格变化通知:在电商系统中,商品价格的变化是一个常见的情况。当商品价格发生变化时,需要及时通知关注该商品的用户,以便他们能够做出购买决策。同时,价格变化也可能会影响到其他相关的业务逻辑,如促销活动、价格比较等。
假设我们有一个电商系统,其中包含商品类Product作为具体主题,以及用户类User作为具体观察者。当商品价格发生变化时,Product类会通知所有关注该商品的用户。
import java.util.ArrayList;
import java.util.List;// 观察者接口
interface Observer {void update(double price);
}// 被观察者类(商品类)
class Product {private List<Observer> observers = new ArrayList<>();private double price;public void addObserver(Observer observer) {observers.add(observer);}public void removeObserver(Observer observer) {observers.remove(observer);}private void notifyObservers() {for (Observer observer : observers) {observer.update(price);}}public void setPrice(double price) {this.price = price;System.out.println("商品价格更新为: " + price);notifyObservers();}
}// 用户类(观察者)
class User implements Observer {private String name;public User(String name) {this.name = name;}@Overridepublic void update(double price) {System.out.println(name + " 收到商品价格更新通知,当前价格为: " + price);}
}
在上述代码中,Product类维护了一个观察者列表,当价格发生变化时,会调用notifyObservers方法通知所有观察者。User类实现了Observer接口,在update方法中接收价格更新通知并进行相应的处理。
4.2 游戏开发中的应用
在游戏开发中,观察者模式同样发挥着重要的作用,它能够帮助开发者实现游戏元素之间的解耦,提高游戏的可维护性和可扩展性。下面我们将详细探讨观察者模式在游戏开发中的两个重要应用场景:游戏角色状态变化通知和游戏事件处理。
游戏角色状态变化通知:在游戏中,角色的状态变化是一个常见的情况,如生命值、魔法值、等级等的变化。当角色的状态发生变化时,需要及时通知相关的游戏元素,如界面显示、技能系统、队友等,以便它们能够做出相应的反应。
假设我们有一个角色扮演游戏,其中包含角色类Character作为具体主题,以及界面显示类UI和技能系统类SkillSystem作为具体观察者。当角色的生命值发生变化时,Character类会通知所有注册的观察者。
import java.util.ArrayList;
import java.util.List;// 观察者接口
interface Observer {void update(int health);
}// 被观察者类(角色类)
class Character {private List<Observer> observers = new ArrayList<>();private int health;public void addObserver(Observer observer) {observers.add(observer);}public void removeObserver(Observer observer) {observers.remove(observer);}private void notifyObservers() {for (Observer observer : observers) {observer.update(health);}}public void setHealth(int health) {this.health = health;System.out.println("角色生命值更新为: " + health);notifyObservers();}
}// 界面显示类(观察者)
class UI implements Observer {@Overridepublic void update(int health) {System.out.println("界面显示角色生命值为: " + health);}
}// 技能系统类(观察者)
class SkillSystem implements Observer {@Overridepublic void update(int health) {if (health <= 0) {System.out.println("角色已死亡,禁用技能");} else {System.out.println("角色生命值正常,技能可用");}}
}
在上述代码中,Character类维护了一个观察者列表,当生命值发生变化时,会调用notifyObservers方法通知所有观察者。UI和SkillSystem类实现了Observer接口,在update方法中根据生命值的变化进行相应的处理。
游戏事件处理:在游戏中,各种事件的发生是不可避免的,如玩家点击、碰撞检测、任务完成等。当这些事件发生时,需要及时通知相关的游戏元素,如游戏逻辑、音效系统、动画系统等,以便它们能够做出相应的反应。
假设我们有一个动作游戏,其中包含事件管理器类EventManager作为具体主题,以及游戏逻辑类GameLogic和音效系统类SoundSystem作为具体观察者。当玩家点击事件发生时,EventManager类会通知所有注册的观察者。
import java.util.ArrayList;
import java.util.List;// 观察者接口
interface Observer {void handleClick();
}// 被观察者类(事件管理器类)
class EventManager {private List<Observer> observers = new ArrayList<>();public void addObserver(Observer observer) {observers.add(observer);}public void removeObserver(Observer observer) {observers.remove(observer);}private void notifyObservers() {for (Observer observer : observers) {observer.handleClick();}}public void onPlayerClick() {System.out.println("玩家点击事件发生");notifyObservers();}
}// 游戏逻辑类(观察者)
class GameLogic implements Observer {@Overridepublic void handleClick() {System.out.println("游戏逻辑处理玩家点击事件");}
}// 音效系统类(观察者)
class SoundSystem implements Observer {@Overridepublic void handleClick() {System.out.println("音效系统播放点击音效");}
}
在上述代码中,EventManager类维护了一个观察者列表,当玩家点击事件发生时,会调用notifyObservers方法通知所有观察者。GameLogic和SoundSystem类实现了Observer接口,在handleClick方法中根据玩家点击事件进行相应的处理。
4.3 消息推送系统中的应用
在消息推送系统中,观察者模式是实现高效、灵活消息传递的核心机制。它能够满足不同用户对于不同类型消息的订阅需求,确保消息能够准确、及时地推送给目标用户。下面我们将详细探讨观察者模式在消息推送系统中的三个重要应用场景:用户订阅、消息发布和推送。
用户订阅:在消息推送系统中,用户可以根据自己的兴趣和需求,订阅不同类型的消息,如新闻资讯、社交媒体动态、促销活动等。用户订阅的过程,实际上就是将自己注册为相应消息主题的观察者。
假设我们有一个消息推送系统,其中包含消息主题类MessageTopic作为具体主题,以及用户类User作为具体观察者。用户可以通过调用MessageTopic类的subscribe方法,将自己订阅到感兴趣的消息主题。
import java.util.ArrayList;
import java.util.List;// 观察者接口
interface Observer {void receiveMessage(String message);
}// 被观察者类(消息主题类)
class MessageTopic {private List<Observer> observers = new ArrayList<>();private String topicName;public MessageTopic(String topicName) {this.topicName = topicName;}public void subscribe(Observer observer) {observers.add(observer);System.out.println(observer + " 已订阅 " + topicName + " 主题");}public void unsubscribe(Observer observer) {observers.remove(observer);System.out.println(observer + " 已取消订阅 " + topicName + " 主题");}private void notifyObservers(String message) {for (Observer observer : observers) {observer.receiveMessage(message);}}public void publishMessage(String message) {System.out.println("发布 " + topicName + " 主题消息: " + message);notifyObservers(message);}
}// 用户类(观察者)
class User implements Observer {private String name;public User(String name) {this.name = name;}@Overridepublic void receiveMessage(String message) {System.out.println(name + " 收到消息: " + message);}@Overridepublic String toString() {return name;}
}
在上述代码中,MessageTopic类维护了一个观察者列表,用户通过subscribe方法将自己添加到列表中,实现订阅操作。当用户不再需要接收该主题的消息时,可以通过unsubscribe方法取消订阅。
消息发布:在消息推送系统中,消息发布者负责创建和发布各种类型的消息。当消息发布者发布消息时,会通知所有订阅该消息主题的用户。
假设我们有一个消息推送系统,其中包含消息发布者类MessagePublisher,它负责创建和发布消息。消息发布者通过调用MessageTopic类的publishMessage方法,将消息发布给所有订阅该主题的用户。
// 消息发布者类
class MessagePublisher {private MessageTopic messageTopic;public MessagePublisher(MessageTopic messageTopic) {this.messageTopic = messageTopic;}public void publish(String message) {messageTopic.publishMessage(message);}
}
在上述代码中,MessagePublisher类持有一个MessageTopic对象的引用,通过调用其publishMessage方法,将消息发布给所有订阅该主题的用户。
消息推送:在消息推送系统中,当消息发布后,系统需要将消息推送给所有订阅该消息主题的用户。这一过程通过调用观察者的receiveMessage方法来实现。
假设我们有一个消息推送系统,其中包含消息主题类MessageTopic和用户类User。当消息发布后,MessageTopic类会遍历观察者列表,调用每个用户的receiveMessage方法,将消息推送给用户。
public class MessagePushSystem {public static void main(String[] args) {MessageTopic newsTopic = new MessageTopic("新闻资讯");User user1 = new User("张三");User user2 = new User("李四");newsTopic.subscribe(user1);newsTopic.subscribe(user2);MessagePublisher publisher = new MessagePublisher(newsTopic);publisher.publish("今日头条:科技创新引领未来");newsTopic.unsubscribe(user2);publisher.publish("最新消息:行业动态一览");}
}
在上述代码中,MessagePushSystem类演示了消息推送系统的使用过程。用户首先订阅感兴趣的消息主题,然后消息发布者发布消息,系统将消息推送给订阅用户。当用户取消订阅后,将不再接收该主题的消息。
五、观察者模式的优势与挑战
5.1 显著优势
- 解耦对象关系:观察者模式最大的优势之一就是解耦了主题和观察者之间的依赖关系。主题无需了解具体观察者的实现细节,只需要维护一个观察者列表,并在状态变化时通知它们即可。观察者也只需要关注主题的抽象接口,而不需要直接依赖于主题的具体实现。这种解耦使得系统更加灵活,易于维护和扩展。例如,在电商系统中,商品价格的变化通知和库存变化通知可以分别独立地添加或移除观察者,而不会影响到其他部分的代码。
- 提高可维护性:由于对象之间的解耦,当主题或观察者的实现发生变化时,不会对其他对象产生直接影响。这使得系统的维护变得更加容易,因为我们可以独立地修改和测试主题和观察者的代码,而不用担心会破坏其他部分的功能。例如,在游戏开发中,如果需要修改游戏角色的状态变化通知逻辑,只需要在角色类中进行修改,而不会影响到其他依赖于角色状态变化的游戏元素。
- 增强可扩展性:观察者模式使得系统具有良好的扩展性。我们可以在运行时动态地添加或移除观察者,而不需要修改主题的代码。这使得系统能够轻松地适应不断变化的需求。例如,在消息推送系统中,新的用户可以随时订阅感兴趣的消息主题,而不需要对消息发布者的代码进行修改。
- 支持广播通信:观察者模式天然支持广播通信,当主题状态发生变化时,所有注册的观察者都会收到通知。这种广播机制使得系统能够高效地将信息传递给多个对象,而不需要逐个通知。例如,在气象站发布天气信息时,所有订阅了该气象站的用户都能同时收到天气变化的通知。
5.2 面临的挑战
-
性能开销:当观察者数量众多时,通知所有观察者可能会带来较大的性能开销。特别是在主题状态频繁变化的情况下,这种开销可能会更加明显。为了缓解性能问题,可以考虑采用异步通知的方式,将通知操作放到单独的线程中执行,这样可以避免阻塞主线程。另外,可以根据实际需求,对观察者进行分组管理,只通知那些真正需要更新的观察者,减少不必要的通知操作。
-
通知顺序问题:在观察者模式中,通常不会对通知观察者的顺序做出严格规定。这可能会导致在某些情况下,观察者的执行顺序会影响到程序的行为。例如,在一个电商系统中,当商品价格发生变化时,可能需要先通知库存管理模块更新库存,再通知促销模块调整促销策略。如果通知顺序错误,可能会导致库存和促销策略的不一致。为了解决通知顺序问题,可以为观察者设置优先级,按照优先级的高低来通知观察者。或者在观察者的实现中,增加一些逻辑来确保正确的执行顺序。
-
循环依赖:如果主题和观察者之间存在复杂的依赖关系,可能会导致循环依赖的问题。例如,观察者在接收到通知后,又反过来修改主题的状态,从而导致主题再次通知观察者,形成无限循环。为了避免循环依赖,在设计系统时,应该尽量保持主题和观察者之间的单向依赖关系,避免观察者对主题进行不必要的修改。如果确实需要在观察者中修改主题的状态,可以增加一些条件判断,确保不会引发循环依赖。
六、观察者模式与其他设计模式的关联
6.1 与 MVC 模式的关系
MVC(Model - View - Controller)模式是一种广泛应用于软件设计中的架构模式,它将应用程序分为三个主要部分:模型(Model)、视图(View)和控制器(Controller)。模型表示应用程序的数据和业务逻辑,视图负责显示数据给用户,控制器则处理用户输入并协调模型和视图之间的交互。
观察者模式在 MVC 架构中扮演着重要的角色,它主要用于实现模型与视图的解耦。在 MVC 模式中,模型可以看作是被观察的主题,而视图则是观察者。当模型的状态发生变化时,它会通知所有注册的视图,视图接收到通知后会自动更新显示,以反映模型的最新状态。这种机制使得模型和视图之间的依赖关系更加松散,提高了系统的可维护性和可扩展性。
例如,在一个简单的用户管理系统中,用户信息存储在模型中,而用户列表页面和用户详情页面则是视图。当用户信息发生变化时,模型会
通知用户列表页面和用户详情页面,这两个视图会根据新的用户信息更新显示。通过观察者模式,模型不需要了解具体的视图实现,只需要关注自身的业务逻辑,而视图也可以独立地进行修改和扩展,而不会影响到模型和其他视图。
具体来说,在 MVC 模式中使用观察者模式的实现步骤如下:
- 定义抽象主题和抽象观察者:在模型中定义抽象主题接口,包含注册、移除和通知观察者的方法。在视图中定义抽象观察者接口,包含更新方法。
- 实现具体主题和具体观察者:模型实现抽象主题接口,维护一个观察者列表,并在状态变化时通知观察者。视图实现抽象观察者接口,在更新方法中根据模型的状态变化更新显示。
- 建立关联:在控制器中,将视图注册到模型中,建立模型和视图之间的观察者关系。当模型的状态发生变化时,会自动通知相关的视图进行更新。
6.2 与发布 - 订阅模式的异同
发布 - 订阅模式(Publish - Subscribe Pattern)与观察者模式在概念上有很多相似之处,它们都涉及到对象之间的一对多依赖关系,以及当一个对象状态发生变化时通知其他对象的机制。然而,它们之间也存在一些重要的区别。
相同点:
- 依赖关系:两者都定义了一种一对多的依赖关系,一个对象(发布者 / 主题)的状态变化会通知到多个其他对象(订阅者 / 观察者)。
- 通知机制:都具备通知机制,当发布者 / 主题的状态发生变化时,会触发通知,告知订阅者 / 观察者进行相应的处理。
不同点:
- 解耦程度:观察者模式中,主题和观察者之间存在直接的依赖关系,主题需要维护一个观察者列表,并直接调用观察者的更新方法。而发布 - 订阅模式中,发布者和订阅者之间通过消息队列或中间件进行通信,它们之间没有直接的依赖关系,解耦程度更高。
- 通信方式:观察者模式通常是同步通信,当主题状态变化时,会立即通知所有观察者。而发布 - 订阅模式可以是异步通信,发布者将消息发送到消息队列后,订阅者可以在合适的时机从队列中获取消息并进行处理,这种异步方式可以提高系统的性能和响应速度。
- 应用场景:观察者模式更适用于单个应用程序内部的模块之间的通信,例如 MVC 架构中模型和视图的交互。而发布 - 订阅模式更适用于不同应用程序之间、分布式系统中的组件之间的通信,例如消息中间件在微服务架构中的应用。
在实际应用中,选择观察者模式还是发布 - 订阅模式,需要根据具体的需求和场景来决定。如果需要实现紧密耦合的、同步的通知机制,并且应用场景主要在单个应用程序内部,可以选择观察者模式。如果需要实现高度解耦的、异步的通信机制,并且应用场景涉及到不同应用程序之间或分布式系统中的组件通信,可以选择发布 - 订阅模式。
七、总结与展望
7.1 回顾与总结
观察者模式作为一种重要的行为型设计模式,在软件开发领域中占据着举足轻重的地位。它通过定义对象之间的一对多依赖关系,实现了对象间的解耦,使得当一个对象的状态发生变化时,所有依赖于它的对象都能自动收到通知并进行更新。这种模式的核心原理在于,将主题(被观察者)和观察者分离,主题负责维护观察者列表并在状态改变时通知观察者,而观察者则实现相应的更新接口来响应主题的变化。
在 Java 中,我们既可以手动实现观察者模式,通过定义抽象主题、具体主题、抽象观察者和具体观察者等角色,构建起完整的观察者体系;也可以使用 JDK 内置的观察者模式,借助java.util.Observable类和java.util.Observer接口来简化实现过程。两种方式各有优劣,手动实现灵活性高,能深入理解模式原理,但代码量较大;JDK 内置实现代码简洁,易于使用,但灵活性受限。
观察者模式在众多实际项目场景中都有着广泛的应用。在电商系统中,它用于商品库存管理和价格变化通知,确保各个相关模块能够及时获取商品信息的变动;在游戏开发中,它被应用于游戏角色状态变化通知和游戏事件处理,实现游戏元素之间的有效交互;在消息推送系统中,它实现了用户订阅、消息发布和推送的功能,满足用户个性化的消息接收需求。
观察者模式的优势显著,它解耦了对象关系,提高了系统的可维护性和可扩展性,同时支持广播通信,使得信息能够高效地传递给多个对象。然而,它也面临一些挑战,如性能开销、通知顺序问题和循环依赖等,需要我们在实际应用中加以注意和解决。
此外,观察者模式与其他设计模式也存在着紧密的关联。在 MVC 模式中,它用于实现模型与视图的解耦,使得模型的变化能够及时反映在视图上;与发布 - 订阅模式相比,虽然两者有相似之处,但在解耦程度、通信方式和应用场景等方面存在差异。
7.2 未来发展趋势
随着软件开发技术的不断发展和演进,观察者模式也将在未来展现出更加广阔的应用前景和发展趋势。在分布式系统和微服务架构日益普及的今天,观察者模式将继续发挥其在实现组件间通信和状态同步方面的优势。通过将观察者模式与消息队列、分布式缓存等技术相结合,可以实现更加高效、可靠的分布式事件通知和数据更新机制,满足大规模分布式系统对实时性和一致性的要求。
在人工智能和大数据领域,观察者模式也将有着重要的应用。例如,在机器学习模型的训练过程中,我们可以使用观察者模式来实时监控模型的训练进度、准确率等指标,并及时通知相关的监控系统或用户。在大数据处理中,当数据发生变化时,通过观察者模式可以自动触发数
据清洗、分析和可视化等后续操作,实现数据处理流程的自动化和智能化。
同时,随着编程语言和开发框架的不断发展,观察者模式的实现方式也将更加多样化和便捷。未来的编程语言和框架可能会提供更加简洁、高效的语法和工具来支持观察者模式的实现,进一步降低开发者的学习成本和开发难度。
观察者模式作为一种经典的设计模式,不仅在当前的软件开发中发挥着重要作用,而且在未来的技术发展中也将持续闪耀光芒。希望读者能够深入理解观察者模式的原理和应用,在实际项目中灵活运用,不断探索和创新,为软件开发事业贡献自己的力量。
相关文章:
解锁观察者模式:Java编程中的高效事件管理之道
系列文章目录 后续补充~~~ 文章目录 一、引言:探索观察者模式的奥秘二、观察者模式的核心原理2.1 模式定义与概念2.2 关键角色剖析2.3 工作机制深度解析 三、观察者模式在 Java 中的实现3.1 手动实现观察者模式3.2 使用 JDK 内置的观察者模式3.3 代码示例解析与对比…...
Ubuntu编译ZLMediaKit
下载 git clone https://gitee.com/xia-chu/ZLMediaKit cd ZLMediaKit git submodule update --init安装工具 sudo apt install -y build-essential sudo apt install -y gcc g sudo apt install -y cmakesudo apt install -y build-essential cmake git libssl-dev libsdl1.…...
全面指南:使用JMeter进行性能压测与性能优化(中间件压测、数据库压测、分布式集群压测、调优)
目录 一、性能测试的指标 1、并发量 2、响应时间 3、错误率 4、吞吐量 5、资源使用率 二、压测全流程 三、其他注意点 1、并发和吞吐量的关系 2、并发和线程的关系 四、调优及分布式集群压测(待仔细学习) 1.线程数量超过单机承载能力时的解决…...
鸿蒙初学者学习手册(HarmonyOSNext_API14)_自定义动画API(@ohos.animator (动画) )
前言 在纯血鸿蒙中最具有用户特色的效果就是自定义的动画效果。在纯血鸿蒙中有多种定义方式,但是今天介绍的是ApI中的自定义动画。 注意: 动画本身具有生命周期,但是不支持在UIAbility的文件使用,简单而言就是不允许在UIAbility生命周期中…...
Grok 3.0 Beta 版大语言模型评测
2025年2月17日至18日,全球首富埃隆马斯克(Elon Musk)携手其人工智能公司xAI,在美国重磅发布了Grok 3.0 Beta版。这款被誉为“迄今为止世界上最智能的语言模型”的AI,不仅集成了先进的“DeepSearch”搜索功能࿰…...
IDEA中查询Maven项目的依赖树
在Maven项目中,查看项目的依赖树是一个常见的需求,特别是当你需要了解项目中直接或间接依赖了哪些库及其版本时。你可以通过命令行使用Maven的dependency:tree插件来做到这一点。这个命令会列出项目中所有依赖的树状结构。 打开idea项目的终端ÿ…...
学习aigc
DALLE2 论文 Hierarchical Text-Conditional Image Generation with CLIP Latents [2204.06125] Hierarchical Text-Conditional Image Generation with CLIP LatentsAbstract page for arXiv paper 2204.06125: Hierarchical Text-Conditional Image Generation with CLIP L…...
springboot整合mybatis-plus【详细版】
目录 一,简介 1. 什么是mybatis-plus2.mybatis-plus特点 二,搭建基本环境 1. 导入基本依赖:2. 编写配置文件3. 创建实体类4. 编写controller层5. 编写service接口6. 编写service层7. 编写mapper层 三,基本知识介绍 1. 基本注解 T…...
【2024 CSDN博客之星】大学四年,我如何在CSDN实现学业与事业的“双逆袭”?
前言: Hello大家好,我是Dream。不知不觉2024年已经过去,自己也马上迈入23岁,感慨时间飞快,从19岁刚入大学加入CSDN,到现在大学毕业已经整整四年了。CSDN陪伴我走过了最青涩的四年大学时光,在这里…...
在VS中通过vcpkg包管理器来安装使用qt5
常用指令 .\vcpkg install 库名 .\vcpkg install 库名版本号.\vcpkg install 库名 --trip x86-windows.\vcpkg list.\vcpkg search 库名 .\vcpkg x-all-installed --7zip PS G:\vcpkg> .\vcpkg help usage: vcpkg <command> [--switches] [--optionsvalues] [argume…...
【C++篇】树影摇曳,旋转无声:探寻AVL树的平衡之道
文章目录 从结构到操作:手撕AVL树的实现一、AVL树介绍1.1 什么是AVL树1.2 平衡因子的定义1.3 平衡的意义1.4 AVL树的操作 二、AVL树的节点结构2.1 节点结构的定义: 三、插入操作3.1 插入操作概述3.2 步骤1:按二叉查找树规则插入节点3.3 步骤2…...
CPU、SOC、MPU、MCU--详细分析四者的区别
一、CPU 与SOC的区别 1.CPU 对于电脑,我们经常提到,处理器,内存,显卡,硬盘四大部分可以组成一个基本的电脑。其中的处理器——Central Processing Unit(中央处理器)。CPU是一台计算机的运算核…...
nacos编写瀚高数据库插件
1、下载nacos源码 git clone gitgithub.com:alibaba/nacos.git 2、引入瀚高驱动 <dependency><groupId>com.highgo</groupId><artifactId>jdbc</artifactId><version>${highgo.version}</version></dependency> 3、DataSource…...
使用excel中的VBA合并多个excel文件
需求是这样的: 在Windows下,用excel文件让多个小组填写了统计信息,现在我需要把收集的多个文件汇总到一个文件中,前三行为标题可以忽略,第四行为收集信息的列名,处理每一行数据的时候,发现某一行…...
linux 安装启动zookeeper全过程及遇到的坑
1、下载安装zookeeper 参考文章:https://blog.csdn.net/weixin_48887095/article/details/132397448 2、启动失败 1、启动失败JAVA_HOME is not set and java could not be found in PATH 已安装 JAVA 配置了JAVA_HOME,还是报错解决方法:参考…...
JAVA JUC 并发编程学习笔记(一)
文章目录 JUC进程概述对比 线程创建线程ThreadRunnableCallable 线程方法APIrun startsleep yieldjoininterrupt打断线程打断 park终止模式 daemon不推荐 线程原理运行机制线程调度未来优化 线程状态查看线程 同步临界区syn-ed使用锁同步块同步方法线程八锁 锁原理Monitor字节码…...
内容中台架构下智能推荐系统的算法优化与分发策略
内容概要 在数字化内容生态中,智能推荐系统作为内容中台的核心引擎,承担着用户需求与内容资源精准匹配的关键任务。其算法架构的优化路径围绕动态特征建模与多模态数据融合展开,通过深度强化学习技术实现用户行为特征的实时捕捉与动态更新&a…...
Java 内存区域详解
1 常见面试题 1.1 基本问题 介绍下Java内存区域(运行时数据区)Java对象的创建过程(五步,建议能够默写出来并且要知道每一步虚拟机做了什么)对象的访问定位的两种方式(句柄和直接指针两种方式)…...
jEasyUI 创建学校课程表
jEasyUI 创建学校课程表 引言 随着信息技术的飞速发展,教育行业也迎来了数字化转型的浪潮。学校课程表的创建和管理作为教育信息化的重要组成部分,其效率和准确性直接影响到学校的教学秩序。jEasyUI,作为一款优秀的开源UI框架,凭借其易用性、灵活性和丰富的组件,成为了许…...
利用 OpenCV 进行棋盘检测与透视变换
利用 OpenCV 进行棋盘检测与透视变换 1. 引言 在计算机视觉领域,棋盘检测与透视变换是一个常见的任务,广泛应用于 摄像机标定、文档扫描、增强现实(AR) 等场景。本篇文章将详细介绍如何使用 OpenCV 进行 棋盘检测,并…...
git-提交时间和作者时间的区别
1.介绍 定义介绍 提交时间(Committer Date):决定了提交在 Git 历史中的位置,通常影响 GitHub 上提交显示的顺序。 作者时间(Author Date):虽然不影响提交的排序,但在每个提交详情页…...
解决双系统开机显示gnu grub version 2.06 Minimal BASH Like Line Editing is Supported
找了好多教程都没有用,终于解决了!!我是因为ubuntu分区的时候出问题了 问题描述: 双系统装好,隔天开机找不到引导项,黑屏显示下列 因为我用的D盘划分出来的部分空闲空间,而不是全部,…...
基于Flask的京东商品信息可视化分析系统的设计与实现
【Flask】基于Flask的京东商品信息可视化分析系统的设计与实现(完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 系统能够灵活地执行SQL查询,提取出用于分析的关键数据指标。为了将这…...
期权帮|股指期货中的套期保值如何操作?
锦鲤三三每日分享期权知识,帮助期权新手及时有效地掌握即市趋势与新资讯! 股指期货中的套期保值如何操作? 一、股指期货中的套期保值准备阶段 确定套保需求,投资者依据市场预判与投资组合分析,决定是否套保。 &…...
用Chrome Recorder轻松完成自动化测试脚本录制
前言 入门自动化测试,录制回放通常是小白测试首先用到的功能。而录制回放工具也一直是各大Web自动化测试必然会着重提供的一块功能。 早期WinRunner、QTP这样的工具,自动化测试可以说是围绕录制回放开展的。近年像Selenium也提供有录制工具 Selenium IDE,Playwright也包含…...
C/C++面试知识点总结
目录 1. 指针1.1 智能指针1.2 指针和引用的区别1.3 数组和指针的区别1.4 数组指针和指针数组的区别1.5 迭代器和指针的区别1.6 strcpy 和 memcpy 的区别 2. 内存管理与分配2.1 内存分配与存储区2.2 malloc / free2.3 volatile和extern的区别2.4 拷贝构造函数2.5 预处理、编译、…...
springboot三层架构详细讲解
目录 springBoot三层架构 0.简介1.各层架构 1.1 Controller层1.2 Service层1.3 ServiceImpl1.4 Mapper1.5 Entity1.6 Mapper.xml 2.各层之间的联系 2.1 Controller 与 Service2.2 Service 与 ServiceImpl2.3 Service 与 Mapper2.4 Mapper 与 Mapper.xml2.5 Service 与 Entity2…...
助力DeepSeek私有化部署服务:让企业AI落地更简单、更安全
在数字化转型的浪潮中,越来越多的企业选择私有化部署AI技术,以保障数据安全、提升业务效率并实现自主可控。DeepSeek作为行业领先的AI开源技术,其技术可以支持企业私有化部署,企业需要一站式服务私有化部署,涵盖硬件采…...
Mac book Air M2 用VMware安装 Ubuntu22.04
安装 VMware Fusion 下载 Ubuntu 安装VMware 完成之后运行新建 将对应Ubuntu 版本拖拽 如图 选择第一个回车 选绿色 回车 为空 相关命令行 sudo apt install net-tools sudo apt install ubuntu-desktop sudo reboot 常用命令行 uname uname -a clear ll ifconfig (查…...
Spring Boot接收参数的19种方式
Spring Boot是一个强大的框架,允许开发人员通过多种方式接收和处理参数。无论是HTTP请求参数、路径变量,还是请求体中的数据,Spring Boot都能提供灵活的处理方式。本文将介绍19种不同的方式来接收参数。 1. 查询参数(Query Param…...
Linux firewalld 常用命令
本文参考RedHat官网文章How to configure a firewall on Linux with firewalld。 Firewalld 是守护进程名,对应命令为firewall-cmd。帮助详见以下命令: $ firewall-cmd --helpUsage: firewall-cmd [OPTIONS...]General Options-h, --help Pr…...
火语言RPA--Excel插入空行
【组件功能】:在Excel内指定的位置插入空行 配置预览 配置说明 在第n行之前 支持T或# 填写添加插入第n行之前行号。 插入n行 支持T或# 插入多少行。 Sheet页名称 支持T或# Excel表格工作簿名称。 示例 Excel插入空行 描述 在第3行之后插入3行。 配置 输…...
纷析云开源版- Vue2-增加字典存储到localStorage
main.js //保存字典数据到LocalStorage Vue.prototype.$api.setting.SystemDictType.all().then(({data}) > {loadDictsToLocalStorage(data) })新增 dictionary.js 放在 Utils文件夹里面 // 获取字典数据 export function getDictByType(dictType) {const dicts JSON.par…...
LangChain-基础(prompts、序列化、流式输出、自定义输出)
LangChain-基础 我们现在使用的大模型训练数据都是基于历史数据训练出来的,它们都无法处理一些实时性的问题或者一些在训练时为训练到的一些问题,解决这个问题有2种解决方案 基于现有的大模型上进行微调,使得它能适应这些问题(本…...
机器学习在脑卒中预测中的应用:不平衡数据集处理方法详解
机器学习在脑卒中预测中的应用:不平衡数据集处理方法详解 目录 引言 脑卒中的全球影响机器学习在医疗预测中的挑战类别不平衡问题的核心痛点数据预处理与特征选择 数据来源与清洗缺失值处理方法类别特征编码特征选择技术处理类别不平衡的四大方法 SMOTE(合成少数类过采样技术…...
Spring Boot项目@Cacheable注解的使用
Cacheable 是 Spring 框架中用于缓存的注解之一,它可以帮助你轻松地将方法的结果缓存起来,从而提高应用的性能。下面详细介绍如何使用 Cacheable 注解以及相关的配置和注意事项。 1. 基本用法 1.1 添加依赖 首先,确保你的项目中包含了 Spr…...
飞书API
extend目录下,API <?php // ---------------------------------------------------------------------- // | 飞书API // ---------------------------------------------------------------------- // | COPYRIGHT (C) 2021 http://www.jeoshi.com All rights reserved. …...
杨校老师课堂之信息学奥赛结构体操作使用经典题集锦汇总
C基础:结构体数组综合训练 员工信息处理系统题目描述输入描述输出描述解题思路参考代码 员工信息处理系统 题目描述 在一家企业中,员工信息的准确性和时效性是日常人事管理工作的关键。由于企业员工数量众多,手动统计与更新员工信息不仅耗费大量时间&a…...
交互编程工具之——Jupyter
Jupyter 是什么? Jupyter 是一个开源的交互式编程和数据分析工具,广泛应用于数据科学、机器学习、教育和研究领域。其核心是 Jupyter Notebook(现升级为 JupyterLab),允许用户在一个基于浏览器的界面中编写代码、运行…...
Redis常见问题排查
redis连接不上去,ERR max number of clients reached redis默认最大连接是10000,如果出现连接泄露或者被服务器被攻击可能会出现连接数超过限制。 Redis 的 INFO 命令可以提供服务器的统计信息,其中包括当前客户端连接数。这是获取连接数最…...
Hadoop初体验
一、HDFS初体验 1. shell命令操作 hadoop fs -mkdir /itcast hadoop fs -put zookeeper.out /itcast hadoop fs -ls / 2. Web UI页面操作 结论: HDFS本质就是一个文件系统有目录树结构 和Linux类似,分文件、文件夹为什么上传一个小文件也这…...
深入解析C++26 Execution Domain:设计原理与实战应用
一、Domain设计目标与核心价值 Domain是C26执行模型的策略载体,其核心解决两个问题: 执行策略泛化:将线程池、CUDA流等异构调度逻辑抽象为统一接口策略组合安全:通过类型隔离避免不同执行域的策略污染 // Domain类型定义示例&a…...
基于Flask的租房信息可视化系统的设计与实现
【Flask】基于Flask的租房信息可视化系统的设计与实现(完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 随着互联网的快速发展,租房市场日益繁荣,信息量急剧增加ÿ…...
TensorFlow v2.16 Overview
TensorFlow v2.16 Overview 一、模块 Modules二、类 Classes三、函数 Functions TensorFlow v2.16.1 Overview 一、模块 Modules 模块是TensorFlow中组织代码的一种方式,将相关的功能和类封装在一起,方便用户使用和管理。每个模块都提供了特定领域的公共…...
网页版的俄罗斯方块
1、新建一个txt文件 2、打开后将代码复制进去保存 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>俄…...
Vue3 状态管理 - Pinia
目录 1. 什么是Pinia 2. 手动添加Pinia到Vue项目 3. Pinia的基础使用 4. getters实现 5. action异步实现 6. storeToRefs工具函数 7. Pinia的调试 8. Pinia的持久化插件 1. 什么是Pinia Pinia 是 Vue 专属的最新状态管理库 ,是 Vuex 状态管理工具的替代品 …...
Arduino 第十六章:pir红外人体传感器练习
Arduino 第十六章:PIR 传感器练习 一、引言 在 Arduino 的众多有趣项目中,传感器的应用是非常重要的一部分。今天我们要学习的主角是 PIR(被动红外)传感器。PIR 传感器能够检测人体发出的红外线,常用于安防系统、自动…...
伯克利 CS61A 课堂笔记 10 —— Trees
本系列为加州伯克利大学著名 Python 基础课程 CS61A 的课堂笔记整理,全英文内容,文末附词汇解释。 目录 01 Trees 树 Ⅰ Tree Abstraction Ⅱ Implementing the Tree Abstraction 02 Tree Processing 建树过程 Ⅰ Fibonacci tree Ⅱ Tree Process…...
Springboot 高频面试题
以下是Spring Boot的高频面试题及答案和底层原理解释: 基础概念 什么是Spring Boot,其主要特点是什么? 答案: Spring Boot本质上是一个建立在Spring框架之上的快速应用开发框架。其主要特点包括: 启动器:一…...
从零开始玩转TensorFlow:小明的机器学习故事 2
你好,TensorFlow!——从零开始的第一个机器学习程序 1. 为什么要写这个“Hello, TensorFlow!”? 无论学习什么新语言或新框架,“Hello World!”示例都能帮助我们快速确认开发环境是否就绪,并掌握最基本的使用方式。对…...