观察者模式(Observer)是软件设计中的一种行为模式。
它定义了对象之间的一对多关系,其中如果一个对象改变了状态,所有依赖它的对象都会自动被通知并更新。
这种模式包含了两种主要的角色,即被观察者(Subject)和观察者(Observer)。
被观察者维护了一个观察者列表,并提供了注册和删除观察者的方法,当其状态发生变化时,会遍历观察者列表,通知所有观察者。
观察者则定义了一个更新接口,用于接收被观察者的通知并进行相应的更新操作。
这种模式能够使得对象之间的耦合度降低,同时也能够提高系统的灵活性和扩展性。
简单地说,观察者模式定义了一个一对多的依赖关系,让一个或多个观察者对象监听一个主题对象。这样一来,当被观察者状态发生改变时,需要通知相应的观察者,使这些观察者对象能够自动更新。
一个对象需要将自己的状态改变通知给其它多个对象,一个对象与它的多个依赖对象需要解耦,以便能对其修改,但不会对其它对象产生影响。
模拟一个消息订阅推送的例子,有一个微信公众号服务,不定时发布一些消息,关注公众号就可以收到推送消息,取消关注就收不到推送消息。
首先定义抽象主题(抽象被观察者)接口
/**
* 抽象主题(抽象被观察者)接口
*
* @author jiangkd
* @date 2023/7/28 9:00:10
*/
public interface ISubject {
/**
* 注册一个观察着
*
* @param observer 观察者
*/
void registerObserver(IObserver observer);
/**
* 移除一个观察者
*
* @param observer 观察者
*/
void removeObserver(IObserver observer);
/**
* 通知所有的观察着
*/
void notifyObservers();
}
然后定义我们的抽象观察者接口
/**
* 抽象观察者接口, 所有的观察者需要实现此接口
*
* @author jiangkd
* @date 2023/7/28 9:01:36
*/
public interface IObserver {
/**
* 观察者对象接收到通知后的逻辑处理
*
* @param msg 接收消息
*/
void update(String msg);
}
接下来是具体主题(具体被观察者),也就是具体主题(被观察者)接口的实现类,实现了subject接口,对接口中的三个方法进行了实现,同时有一个List集合,用以保存注册的观察者,等需要通知观察者时,遍历该集合即可。
/**
* 具体主题(具体被观察者)
*
* @author jiangkd
* @date 2023/7/28 10:00:57
*/
@Component
public class WeChatMessage implements ISubject {
private List<IObserver> observerList = new ArrayList<>();
private String message;
@Override
public void registerObserver(IObserver observer) {
observerList.add(observer);
}
@Override
public void removeObserver(IObserver observer) {
if (CollUtil.isNotEmpty(observerList)) {
observerList.remove(observer);
}
}
@Override
public void notifyObservers() {
for (IObserver iObserver : observerList) {
iObserver.update(message);
}
}
/**
* 模拟被观察者的主题更新, 通知所有观察者
*
* @param message 主题更新
*/
public void updateMessage(String message) {
this.message = message;
// 通知所有观察者
notifyObservers();
}
}
继续定义具体的观察者,这里我们模拟两个用户进行了订阅主题
/**
* 具体的观察者1
*
* @author jiangkd
* @date 2023/7/28 10:07:34
*/
@Slf4j
@Component
public class User1 implements IObserver {
/**
* 观察者接收消息, 知道被观察者发生了变化, 自己进行相应的处理, 这里只是测试打印日志而已
*
* @param msg 接收消息
*/
@Override
public void update(String msg) {
log.info("我是具体的观察者之一:{}", this.getClass().getSimpleName());
log.info("被观察者发生变化, 接收消息:{}", msg);
}
}
/**
* 具体的观察者2
*
* @author jiangkd
* @date 2023/7/28 10:09:22
*/
@Slf4j
@Component
public class User2 implements IObserver {
/**
* 观察者接收消息, 知道被观察者发生了变化, 自己进行相应的处理, 这里只是测试打印日志而已
*
* @param msg 接收消息
*/
@Override
public void update(String msg) {
log.info("我是具体的观察者之一:{}", this.getClass().getSimpleName());
log.info("被观察者发生变化, 接收消息:{}", msg);
}
}
最后测试一下
/**
* @author jiangkd
* @date 2023/7/28 10:10:34
*/
@SpringBootTest(classes = DemoApplication.class)
@RunWith(SpringRunner.class)
@Slf4j
public class ObserverTest {
@Resource
WeChatMessage weChatMessage;
@Resource
User1 user1;
@Resource
User2 user2;
@Test
public void test(){
// 绑定被观察者和观察者
weChatMessage.registerObserver(user1);
weChatMessage.registerObserver(user2);
// 模拟被观察者发生变化
weChatMessage.updateMessage("今天推送什么呢?");
}
}
执行结果如下:
2023-07-28 10:15:58.103 INFO 12236 — [ main] d.b.example.design_patterns.gczms.User1 : 我是具体的观察者之一:User1
2023-07-28 10:15:58.104 INFO 12236 — [ main] d.b.example.design_patterns.gczms.User1 : 被观察者发生变化, 接收消息:今天推送什么呢?
2023-07-28 10:15:58.104 INFO 12236 — [ main] d.b.example.design_patterns.gczms.User2 : 我是具体的观察者之一:User2
2023-07-28 10:15:58.104 INFO 12236 — [ main] d.b.example.design_patterns.gczms.User2 : 被观察者发生变化, 接收消息:今天推送什么呢?
记下来我们测试注销一个观察者,首先我们先再次添加一个观察者User3进行订阅主题
/**
* 具体的观察者3
*
* @author jiangkd
* @date 2023/7/28 10:18:46
*/
@Slf4j
@Component
public class User3 implements IObserver {
/**
* 观察者接收消息, 知道被观察者发生了变化, 自己进行相应的处理, 这里只是测试打印日志而已
*
* @param msg 接收消息
*/
@Override
public void update(String msg) {
log.info("我是具体的观察者之一:{}", this.getClass().getSimpleName());
log.info("被观察者发生变化, 接收消息:{}", msg);
}
}
然后测试被观察者发生变化通知到三个订阅者,接着取消User2,只通知User1和User3
/**
* @author jiangkd
* @date 2023/7/28 10:10:34
*/
@SpringBootTest(classes = DemoApplication.class)
@RunWith(SpringRunner.class)
@Slf4j
public class ObserverTest {
@Resource
WeChatMessage weChatMessage;
@Resource
User1 user1;
@Resource
User2 user2;
@Resource
User3 user3;
@Test
public void test2(){
// 绑定被观察者和观察者
weChatMessage.registerObserver(user1);
weChatMessage.registerObserver(user2);
weChatMessage.registerObserver(user3);
// 模拟被观察者发生变化
weChatMessage.updateMessage("今天推送什么呢?");
log.info("================================================");
// 注销其中一个观察者
weChatMessage.removeObserver(user2);
// 模拟被观察者发生变化
weChatMessage.updateMessage("今天天气不错呢!");
}
}
运行结果日志记录如下
2023-07-28 10:23:21.663 INFO 34500 — [ main] d.b.example.design_patterns.gczms.User1 : 我是具体的观察者之一:User1
2023-07-28 10:23:21.664 INFO 34500 — [ main] d.b.example.design_patterns.gczms.User1 : 被观察者发生变化, 接收消息:今天推送什么呢?
2023-07-28 10:23:21.664 INFO 34500 — [ main] d.b.example.design_patterns.gczms.User2 : 我是具体的观察者之一:User2
2023-07-28 10:23:21.664 INFO 34500 — [ main] d.b.example.design_patterns.gczms.User2 : 被观察者发生变化, 接收消息:今天推送什么呢?
2023-07-28 10:23:21.664 INFO 34500 — [ main] d.b.example.design_patterns.gczms.User3 : 我是具体的观察者之一:User3
2023-07-28 10:23:21.664 INFO 34500 — [ main] d.b.example.design_patterns.gczms.User3 : 被观察者发生变化, 接收消息:今天推送什么呢?
2023-07-28 10:23:21.664 INFO 34500 — [ main] d.b.e.d.gczms.ObserverTest : ================================================
2023-07-28 10:23:21.669 INFO 34500 — [ main] d.b.example.design_patterns.gczms.User1 : 我是具体的观察者之一:User1
2023-07-28 10:23:21.671 INFO 34500 — [ main] d.b.example.design_patterns.gczms.User1 : 被观察者发生变化, 接收消息:今天天气不错呢!
2023-07-28 10:23:21.671 INFO 34500 — [ main] d.b.example.design_patterns.gczms.User3 : 我是具体的观察者之一:User3
2023-07-28 10:23:21.671 INFO 34500 — [ main] d.b.example.design_patterns.gczms.User3 : 被观察者发生变化, 接收消息:今天天气不错呢!