观察者模式学习笔记(详细)
一、什么是观察者模式
观察者模式,是定义对象之间的一对多的关系,主要作用是减少对象之间的耦合度,分为两个角色
- 被观察者:其实就是发布者,发布消息通知所有的观察者
- 观察者:接到被观察者发布的消息做出相应的动作
上图中,左边一组是被观察者,右边一组是观察者
-
Subjecct:被观察者抽象类,拥有类成员ObserverList,和三个抽象方法
ObserverList:存放所有的观察者对象
addObserver():向ObserverList中添加观察者对象
delObject():从ObserverList中删除观察者对象
notifyObservers():通知所有的观察者对象 -
SubjectItem:具体的被观察者类,继承Subject接口,有具体的动作方法,调用父类的notifyObject()方法通知所有的观察者
-
Observer:观察者接口,有一个抽象方法update(),供被观察者调用
-
ObserverItem:具体的观察者类,实现Observer类,实现update()方法
二、使用场景
游戏中,被观察者主角有所动作就通知观察者们
支付中,一个商品购买成功,需要执行许多操作(更新订单,发送邮件,更新账户…),这时就可以使用观察者模式来减少耦合
再比如,关注动态,动态更新,需要发送通知给所有的关注者
三、具体实现
简单的一个冒险游戏,勇者做为被观察者,观察者有怪物,食物,NPC
当勇者移动碰到怪物时血量减1,碰到食物时血量加1,碰到NPC时展开剧情
- 编写Observer接口
public interface Observer {
public void update();
}
- 编写Subject抽象类
public abstract class Subject {
//观察者集合
private List<Observer> observerList = new ArrayList<>();
//添加观察者
public void addObserver(Observer observer){
observerList.add(observer);
}
//删除观察者
public void delObserver(Observer observer){
observerList.remove(observer);
}
//通知所有观察者
public void notifyObservers(){
for(Observer observer : observerList){
observer.update();
}
}
}
observerList集合存放了观察者接口,不直接存放观察者类
notifyObservers()方法遍历observerList集合,调用update()方法
通过抽象层减少耦合
- 编写勇者类(被观察者)
public class Hero extends Subject {
static int X = 0;
static int Y = 0;
//勇者移动
public void move(int x, int y){
X = x;
Y = y;
super.notifyObservers();
}
}
Hero类有move()方法,让勇者移动并调用父类的notifyObservers()方法通知所有的观察者
- 编写怪物类(观察者)
public class Monster implements Observer {
static int X = 5;
static int Y = 7;
@Override
public void update() {
if(Hero.X == X && Hero.Y == Y){
System.out.println("怪物攻击勇者!");
}
}
}
Monster类实现Observer接口,实现具体的update()方法
- 编写食物类(观察者)
public class Food implements Observer {
static int X = 8;
static int Y = 8;
@Override
public void update() {
if(Hero.X == X && Hero.Y == Y){
System.out.println("勇者吃到了食物,血量加1!");
}
}
}
Food类实现Observer接口,实现具体的update()方法
- 编写npc类(观察者)
public class Npc implements Observer {
static int X = 15;
static int Y = 20;
@Override
public void update() {
if(Hero.X == X && Hero.Y == Y){
System.out.println("勇者遇到NPC,剧情展开!");
}
}
}
Npc类实现Observer接口,实现具体的update()方法
- 编写执行类
public class Run {
public static void main(String[] args){
//初始化对象
Hero hero = new Hero();
Monster monster = new Monster();
Food food = new Food();
Npc npc = new Npc();
//添加观察者
hero.addObserver(monster);
hero.addObserver(food);
hero.addObserver(npc);
//勇者移动,遇到怪物
hero.move(5,7);
//勇者移动,吃到食物
hero.move(8,8);
//勇者移动,遇到npc
hero.move(15,20);
}
}
勇者移动时通知了所有已注册的观察者,执行各自的update方法
- 运行结果
四、总结
-
优点:
使用抽象的方式来减少被观察者和观察者之间的耦合,可以方便的增加观察者 -
缺点:
如果被观察者有过多的直接观察者和间接观察者的话,会花费更多的时间,如果观察者之间存在循环依赖的话,会导致他们直接进行循环调用,最终导致系统崩溃
观察者可以知道被观察者发生的变化,但是无法知道被观察者是如何发生的变化