咱们或许需求在某个目标的状况
产生改变
时,主动
告诉其他相关的目标更新自己的状况,但是咱们又不希望直接在目标中硬编码这种告诉机制,由于这样会导致代码的可保护性和可扩展性变差。为了处理这种问题,咱们能够运用
调查者形式
。调查者形式界说了一种松耦合的目标通讯机制,使得多个目标能够在一起工作,而又不必互相了解。在调查者形式中,一个目标(称为主题)保护一组依靠于它的目标(称为调查者),并在状况产生改变时主动告诉一切的调查者。这种机制能够使得代码愈加灵敏、可扩展和易于保护,然后进步软件的质量和效率。因而,调查者形式被广泛运用于许多范畴,包括
桌面运用程序
、Web 运用程序
、移动运用程序
等等。经过运用调查者形式,咱们能够在不改动目标原有行为的情况下,动态地增加新的行为,然后使得代码愈加灵敏和易于保护。
什么是调查者形式
官方界说:
调查者形式:界说了一种一对多的依靠联系,当一个目标的状况产生改变时,一切依靠它的目标都会得到告诉并主动更新。
The Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
调查者形式的角色
-
Subject(主题):被调查的目标,保护一个调查者列表,并供给注册、删去和告诉调查者的办法。
-
Observer(调查者):调查主题的目标,包括一个更新自己状况的办法。
-
ConcreteSubject(详细主题):完成主题接口,保护自己的状况并在状况产生改变时告诉调查者。
-
ConcreteObserver(详细调查者):完成调查者接口,包括更新自己状况的详细完成。
调查者形式的完成
事例
想象一下,在一个城市里,有一个气候台和多个手机App、桌面App和网站,它们都需求显现当时的气候情况。在这种情况下,咱们能够运用调查者形式来完成这个功用。 气候台(Subject)保护了当时的气候状况,并供给了一个办法来更新气候数据。手机App、桌面App和网站(Observers)订阅了气候台的气候数据,当气候数据产生改变时,气候台会主动告诉一切的调查者,并更新它们自己的状况。
Subject(主题)
// 主题接口
interface Subject {
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers();
}
Subject(主题)类的办法比较固定,通常会包括以下三个办法:
-
registerObserver(Observer o)
:用于注册一个Observer目标,使得Subject能够向该Observer发送告诉。 -
removeObserver(Observer o)
:用于移除一个现已注册的Observer目标。 -
notifyObservers()
:用于向一切已注册的Observer发送告诉。
Observer(调查者)
// 调查者接口
interface Observer {
void update(double temperature, double humidity, double pressure);
}
- Observer(调查者)一般包括一个办法,即
update()
办法,该办法用于接收Subject发来的告诉,并更新自己的状况。
ConcreteSubject(详细主题)
// 气候台类
class WeatherStation implements Subject {
private double temperature;
private double humidity;
private double pressure;
private List<Observer> observers = new ArrayList<>();
public void setWeather(double temperature, double humidity, double pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
notifyObservers();
}
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
@Override
public void removeObserver(Observer o) {
observers.remove(o);
}
@Override
public void notifyObservers() {
for (Observer o : observers) {
o.update(temperature, humidity, pressure);
}
}
}
ConcreteSubject(详细主题)通常会保护一个Observer列表,用于存储一切现已注册的Observer目标,以便在状况产生改变时能够告诉它们。在详细完成中,
ConcreteSubject需求完成注册Observer、移除Observer和告诉Observer等办法,并在状况产生改变时调用告诉办法告诉一切的Observer更新自己的状况。
WeatherStation
便是一个详细的主题,它保护了一个List集合,用于存储一切现已注册的Observer目标,一起供给了注册Observer、移除Observer和告诉Observer等办法。这种完成方式使得咱们能够动态地增加或删去Observer目标,然后完成更好的灵敏性和扩展性。
ConcreteObserver(详细调查者)
// 手机App、桌面App和网站类
class WeatherApp implements Observer {
private String name;
public WeatherApp(String name) {
this.name = name;
}
@Override
public void update(double temperature, double humidity, double pressure) {
System.out.printf("%s: 当时气候温度为 %.2f ℃,湿度为 %.2f%%,气压为 %.2f kPa\n", name, temperature, humidity, pressure);
}
}
WeatherApp
类完成了Observer接口,偏重写了其间的update()
办法。当WeatherStation
的气候数据产生改变时,它会主动告诉一切现已注册的Observer目标,并调用它们的update()
办法。在WeatherApp
的update()
办法中,它会获取WeatherStation
的当时气候数据,并更新自己的界面显现。
WeatherApp
类扮演的角色是详细的Observer,它完成了Observer接口,并在其间界说了详细的更新逻辑,即依据WeatherStation
的气候数据更新自己的界面显现。一起,WeatherApp
类还能够动态地注册、移除和更新自己的状况,以便更好地习惯不同的运用场景和需求。
客户端
// 测验代码
public class ObserverPatternExample {
public static void main(String[] args) {
WeatherStation weatherStation = new WeatherStation();
WeatherApp app1 = new WeatherApp("手机App1");
WeatherApp app2 = new WeatherApp("桌面App1");
WeatherApp app3 = new WeatherApp("网站1");
weatherStation.registerObserver(app1);
weatherStation.registerObserver(app2);
weatherStation.registerObserver(app3);
weatherStation.setWeather(20.5, 60.3, 101.3);
}
}
调查者形式类图
维基百科
在spring源码中的运用
在Spring结构中,ApplicationContext中的事情、Bean生命周期事情和自界说事情都是调查者形式的详细运用。ApplicationContext作为主题(Subject)保护了一个监听器列表(Observer),用于存储一切现已注册的监听器。当事情产生时,ApplicationContext会主动告诉一切的监听器,并调用它们的事情处理办法(onApplicationEvent办法),以便履行详细的逻辑。这种形式能够协助咱们在Spring运用程序中完成目标之间的动态通讯,以便更好地满意事务需求和用户需求。
-
ApplicationContext中的事情,例如ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、ContextClosedEvent等;
-
Bean生命周期事情,例如BeforeInitializationEvent、AfterInitializationEvent等;
-
自界说事情,例如订单创立事情、付出成功事情、邮件发送成功事情等。
这三个场景都是事情驱动模型,事情驱动模型是一种完成调查者形式的方式,它将调查者形式中的主题(Subject)和调查者(Observer)抽象为事情(Event)和监听器(Listener),并运用事情和监听器之间的联系来完成调查者形式。
代码举例
我来详细说一下自界说事情
举一个订单创立事情的比如。假定咱们正在开发一个电商渠道,咱们需求在订单创立成功时,发送一封承认邮件给用户。为了完成这个功用,咱们能够界说一个OrderCreatedEvent事情,并在订单创立时发布这个事情。详细代码如下:
首要,界说一个OrderCreatedEvent类,承继自ApplicationEvent
类,用于表明订单创立事情:
public class OrderCreatedEvent extends ApplicationEvent {
private Order order;
public OrderCreatedEvent(Object source, Order order) {
super(source);
this.order = order;
}
public Order getOrder() {
return order;
}
}
在订单创立成功时,咱们能够经过ApplicationContext目标来发布一个OrderCreatedEvent
事情,示例代码如下:
@Component
public class OrderService {
@Autowired
private ApplicationContext applicationContext;
public void createOrder(Order order) {
// 创立订单逻辑
...
// 发布订单创立事情
applicationContext.publishEvent(new OrderCreatedEvent(this, order));
}
}
上述代码中,咱们首要经过@Autowired注解注入了ApplicationContext目标,然后在订单创立成功后,经过applicationContext.publishEvent()办法来发布OrderCreatedEvent事情,并将订单信息封装到事情目标中。
接下来,咱们需求界说一个监听器来监听OrderCreatedEvent事情,并在事情产生时发送承认邮件给用户。示例代码如下:
@Component
public class OrderCreatedEventListener implements ApplicationListener<OrderCreatedEvent> {
@Override
public void onApplicationEvent(OrderCreatedEvent event) {
Order order = event.getOrder();
// 发送承认邮件给用户
...
}
}
在Spring结构中,自界说事情是一个十分重要的特性,它能够协助咱们完成各种杂乱事务场景,例如订单创立事情、付出成功事情、邮件发送成功事情等。
举一个订单创立事情的比如。假定咱们正在开发一个电商渠道,咱们需求在订单创立成功时,发送一封承认邮件给用户。为了完成这个功用,咱们能够界说一个OrderCreatedEvent事情,并在订单创立时发布这个事情。详细代码如下:
首要,界说一个OrderCreatedEvent类,承继自ApplicationEvent类,用于表明订单创立事情:
javaCopy code
public class OrderCreatedEvent extends ApplicationEvent {
private Order order;
public OrderCreatedEvent(Object source, Order order) {
super(source);
this.order = order;
}
public Order getOrder() {
return order;
}
}
在订单创立成功时,咱们能够经过ApplicationContext目标来发布一个OrderCreatedEvent事情,示例代码如下:
javaCopy code
@Component
public class OrderService {
@Autowired
private ApplicationContext applicationContext;
public void createOrder(Order order) {
// 创立订单逻辑
...
// 发布订单创立事情
applicationContext.publishEvent(new OrderCreatedEvent(this, order));
}
}
上述代码中,咱们首要经过@Autowired注解注入了ApplicationContext目标,然后在订单创立成功后,经过applicationContext.publishEvent()办法来发布OrderCreatedEvent事情,并将订单信息封装到事情目标中。
接下来,咱们需求界说一个监听器来监听OrderCreatedEvent事情,并在事情产生时发送承认邮件给用户。示例代码如下:
javaCopy code
@Component
public class OrderCreatedEventListener implements ApplicationListener<OrderCreatedEvent> {
@Override
public void onApplicationEvent(OrderCreatedEvent event) {
Order order = event.getOrder();
// 发送承认邮件给用户
...
}
}
在上述代码中,咱们界说了一个OrderCreatedEventListener
监听器,完成了ApplicationListener
接口,偏重写了其间的onApplicationEvent()
办法,用于接收OrderCreatedEvent事情的告诉,并履行相应的逻辑,例如发送承认邮件给用户等。
经过这种方式,咱们能够十分方便地完成订单创立事情的处理逻辑,并将处理逻辑与事务逻辑进行别离,然后使得代码愈加模块化和易于保护。一起,咱们还能够经过界说不同的自界说事情和监听器,来完成各种杂乱的事务场景,例如付出成功事情、邮件发送成功事情等。
什么情况下运用调查者形式
-
当一个目标的改动需求一起改动其他多个目标的时分,运用调查者形式能够防止目标之间的严密耦合。例如,在一个气候运用中,如果要在多个当地显现当时气候状况,咱们能够运用调查者形式,将气候运用作为主题(Subject),将手机App、桌面App和网站作为调查者(Observer),以便在气候数据产生改变时,主动更新一切的调查者。
-
当一个目标的改动会触发连锁反应,引起其他多个目标的状况改变时,运用调查者形式能够协助咱们更好地办理这种连锁反应。例如,在一个电商运用中,如果用户下单成功后,需求触发一系列的事情,例如减少库存、生成订单、发送短信等,咱们能够运用调查者形式,将订单作为主题(Subject),将库存办理器、订单办理器、短信发送器等作为调查者(Observer),以便在订单创立成功后,主动触发这些事情。
-
当一个目标的改动需求动态地增加或删去其他多个目标时,运用调查者形式能够协助咱们更好地办理这些动态改变。例如,在一个交际运用中,如果用户在发布一条动态时,能够选择将这条动态发布到某个圈子或许某些老友,咱们能够运用调查者形式,将用户作为主题(Subject),将圈子和老友作为调查者(Observer),以便在用户选择发布圈子或老友时,动态地增加或删去对应的调查者。
总结:
调查者形式的长处:
-
解耦性好:调查者形式能够将主题(Subject)和调查者(Observer)解耦,然后使得它们之间的依靠联系变得松懈。这样一来,主题和调查者就能够相互独立地改变,而不会对互相形成影响。
-
可扩展性好:调查者形式能够很容易地增加新的调查者,然后完成愈加灵敏的扩展。这样一来,当新的调查者加入时,主题和已有的调查者不需求做任何修正,只需求增加新的调查者即可。
-
便于保护:调查者形式能够将逻辑别离,然后使得代码愈加明晰、简练和易于保护。这样一来,当需求改变时,咱们只需求修正调查者的完成,而不需求修正主题的完成,然后防止了代码的耦合性。
-
易于完成:调查者形式的完成十分简单,只需求界说好主题和调查者的接口,并在主题中保护一个调查者列表,即可完成调查者形式的功用。
调查者形式的缺陷:
-
过多的调查者会导致性能问题:当主题有过多的调查者时,会导致告诉调查者的时刻延伸,然后影响性能。因而,在运用调查者形式时,需求留意调查者的数量,防止过多的调查者。
-
调查者和主题之间的联系或许难以理解:当调查者和主题之间的联系过于杂乱时,或许会导致代码难以理解和保护。因而,在运用调查者形式时,需求留意代码的明晰度和简练度。
总之,调查者形式是一种十分常用的规划形式,它能够协助咱们完成目标之间的松耦合,并使得代码愈加灵敏、可扩展和易于保护。尽管调查者形式存在一些缺陷,但只需合理运用和规划,就能够最大化地发挥其长处,然后满意各种事务需求。