介绍
观察者模式是发布/订阅(pub/sub)模式,是一种推模式。
观察者模式定义了一种一对多的依赖关系,让多个观察者同时监听某个主题对象。主题对象一旦发生改变时,会通知所有观察者对象,使它们能够自动更新自己。
观察者模式也解除了代码的耦合,让耦合的双方都依赖于抽象,而不是直接依赖于具体,从而让各自的变化不会影响另一边的变化。
结构图
Subject类 : 主题对象/抽象通知者,一般用一个接口或者抽象类实现,它把所有的观察者保存到一个集合里,每个主题都可以有任何的数量的观察者。该类为具体通知者定义了一些接口,比如添加、删除观察者和通知观察者事件等
Observer:为抽象观察者定义一个更新接口,在得到主题通知时更新自己
ConcreteSubject:具体主题,是抽象主题的扩展和具体实现。当发生改变时主动通知观察者
ConcreteObserver:具体观察者,是对抽象观察者的扩展和具体实现。当接收到主题通知时更新自己
使用场景
(1)一对多的情况下:一个对象发生改变同时需要改变其它对象
(2) 解除耦合:让耦合的双方都依赖于抽象,而不是依赖于具体。使得各自发生变化都不会影响对方
Spring的监听机制使用了观察者模式
举例实践
举例描述
在一个家庭中有男主人和女主人,还有两条狗子。当男主人或者女主人敲碗的时候,两只狗子听到敲碗就立即摇着尾巴跑过来吃饭。在这个事件中男主人和女主人就是主题通知者,狗子就是观察者,而敲碗就是通知狗狗来吃饭。
图解
主题抽象类owner
public interface Owner {
/**
* 添加宠物
*/
void addPet(Pet pet);
/**
* 移除宠物
*/
void removePet(Pet pet);
/**
* 通知狗狗们过来吃饭
*/
void notifyEat();
}
主题具体类-host
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* 男主人
*/
public class Host implements Owner{
private static final List<Pet> petList = new CopyOnWriteArrayList<>();
@Override
public void addPet(Pet pet) {
petList.add(pet);
}
@Override
public void removePet(Pet pet) {
petList.remove(pet);
}
@Override
public void notifyEat() {
for (Pet pet : petList) {
pet.eat();
}
}
}
主题具体类-hostess
/**
*女主人
*/
public class Hostess implements Owner{
private static final List<Pet> petList = new CopyOnWriteArrayList<>();
@Override
public void addPet(Pet pet) {
petList.add(pet);
}
@Override
public void removePet(Pet pet) {
petList.remove(pet);
}
@Override
public void notifyEat() {
for (Pet pet : petList) {
pet.eat();
}
}
}
观察者抽象类-pet
@FunctionalInterface
public interface Pet {
/**
* 吃饭
*/
void eat();
}
观察者具体类-dog1
public class Dog1 implements Pet{
@Override
public void eat() {
System.out.println("dog1摇着尾巴来吃饭了...");
}
}
观察者具体类-dog2
public class Dog2 implements Pet{
@Override
public void eat() {
System.out.println("dog2摇着尾巴来吃饭了...");
}
}
测试类
public class OTest {
@Test
public void test() {
//创建通知者
Owner host = new Host();
Owner hostess = new Hostess();
//创建观察者
Pet dog1 = new Dog1();
Pet dog2 = new Dog2();
//将观察者注册到通知者中
host.addPet(dog1);
host.addPet(dog2);
hostess.addPet(dog1);
hostess.addPet(dog2);
System.out.println("-------男主人早上敲碗--------");
host.notifyEat();
System.out.println("-------女主人中午敲碗--------");
hostess.notifyEat();
System.out.println("--------下午女主人带着狗狗1除去遛弯了--------");
host.removePet(dog1);
System.out.println("--------下午男主人敲碗啦--------");
host.notifyEat();
}
}
测试结果
-------男主人早上敲碗--------
dog1摇着尾巴来吃饭了...
dog2摇着尾巴来吃饭了...
-------女主人中午敲碗--------
dog1摇着尾巴来吃饭了...
dog2摇着尾巴来吃饭了...
--------下午女主人带着狗狗1除去遛弯了--------
--------下午男主人敲碗啦--------
dog2摇着尾巴来吃饭了...
Process finished with exit code 0
参考
《大话设计模式》. 程杰著