定义
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新发自己。
一个核心类,拥有其它类(观察者类)想要的信息,观察者类向核心类注册后,核心类的信息发生变化时,对订阅者进行广播,把更改的信息发给订阅者,或者通知订阅者来更新信息。
UML类图
小案例
需求
经常玩咸鱼和转转的垃圾佬,应该知道咸鱼和转转都支持拍卖。
咱们模拟一下拍卖流程来理解一下观察者模式。
需要一个拍卖类,记录拍卖物品的信息,当有人出更高价时,进行广播。——核心类,Subject、Auction。
其他用户,接受广播,并有自己心里预期的价格。——观察者类,Observer、User
拍卖模式UML类图
代码
Observer
// 观察者接口,核心业务由 auction 实现
public abstract class Observer {
public String name;
public double price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public abstract void update(Observer topObserver);
public abstract void display();
}
Subject
import java.util.ArrayList;
import java.util.List;
public class Auction implements Subject{
private String name;
private double startPrice;
private Observer topObserver;
private List<Observer> observers;
public Auction() {
observers = new ArrayList<>();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Observer getTopObserver() {
return topObserver;
}
public void setTopObserver(Observer topObserver) {
this.topObserver = topObserver;
priceChange();
}
public double getStartPrice() {
return startPrice;
}
public void setStartPrice(double startPrice) {
this.startPrice = startPrice;
}
public void setData(String name,double startPrice) {
this.name = name;
this.startPrice = startPrice;
priceChange();
}
private void priceChange() {
notifyObservers();
}
@Override
public void registerOvesrver(Observer observer) {
observers.add(observer);
}
@Override
public void notifyObservers() {
for (int i = 0; i < observers.size();i++) {
observers.get(i).update(topObserver);
}
}
}
Subject
// 被观察者接口,核心业务由 auction 实现
public interface Subject {
void registerOvesrver(Observer observer);
void notifyObservers();
}
User1
public class User1 extends Observer{
public String name="张三";
public double price=100.0;
private Observer topObserver;
@Override
public void update(Observer topObserver) {
this.topObserver = topObserver;
display();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public void display() {
if (topObserver==null) {
return;
}
if (name.equals(topObserver.getName())) {
System.out.println("最高价是我"+name);
}else {
System.out.println("张三收到信息:/n最高价:"+
topObserver.getPrice()+
"出价者:"+topObserver.getName());
}
}
}
User2
public class User2 extends Observer{
public String name="李四";
public double price=200.0;
private Observer topObserver;
@Override
public void update(Observer topObserver) {
this.topObserver = topObserver;
display();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public void display() {
if (topObserver==null) {
return;
}
if (name.equals(topObserver.getName())) {
System.out.println("最高价是我"+name);
}else {
System.out.println("李四收到信息:/n最高价:"+
topObserver.getPrice()+
"出价者:"+topObserver.getName());
}
}
}
User3
public class User3 extends Observer{
public String name="王二";
public double price=300.0;
private Observer topObserver;
@Override
public void update(Observer topObserver) {
this.topObserver = topObserver;
display();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public void display() {
if (topObserver==null) {
return;
}
if (name.equals(topObserver.getName())) {
System.out.println("最高价是我"+name);
}else {
System.out.println("王二收到信息:/n最高价:"+
topObserver.getPrice()+
"出价者:"+topObserver.getName());
}
}
}
User4
public class User4 extends Observer{
public String name="麻子";
public double price=400.0;
private Observer topObserver;
@Override
public void update(Observer topObserver) {
this.topObserver = topObserver;
display();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public void display() {
if (topObserver==null) {
return;
}
if (name.equals(topObserver.getName())) {
System.out.println("最高价是我"+name);
}else {
System.out.println("麻子收到信息:/n最高价:"+
topObserver.getPrice()+
"出价者:"+topObserver.getName());
}
}
}
Client
public class Client {
public static void main(String[] args) {
// 竞拍核心类
Auction auction = new Auction();
// 创建观察者
User1 user1 = new User1();
// 注册到竞拍类
auction.registerOvesrver(user1);
User2 user2 = new User2();
auction.registerOvesrver(user2);
User3 user3 = new User3();
auction.registerOvesrver(user3);
User4 user4 = new User4();
auction.registerOvesrver(user4);
System.out.println("设置起拍价");
auction.setData("红米K30 紫玉幻境 6+128", 50.0);
System.out.println("开始竞拍");
auction.setTopObserver(user1);
System.out.println("===");
auction.setTopObserver(user2);
System.out.println("===");
auction.setTopObserver(user3);
System.out.println("===");
auction.setTopObserver(user4);
System.out.println("===");
}
}
运行结果
设置起拍价
开始竞拍
最高价是我张三
李四收到信息:
最高价:100.0出价者:张三
王二收到信息:
最高价:100.0出价者:张三
麻子收到信息:
最高价:100.0出价者:张三
===
张三收到信息:
最高价:200.0出价者:李四
最高价是我李四
王二收到信息:
最高价:200.0出价者:李四
麻子收到信息:
最高价:200.0出价者:李四
===
张三收到信息:
最高价:300.0出价者:王二
李四收到信息:
最高价:300.0出价者:王二
最高价是我王二
麻子收到信息:
最高价:300.0出价者:王二
===
张三收到信息:
最高价:400.0出价者:麻子
李四收到信息:
最高价:400.0出价者:麻子
王二收到信息:
最高价:400.0出价者:麻子
最高价是我麻子
===
优缺点分析
优点
- 观察者和被观察者是抽象耦合的。
- 建立一套触发机制。
缺点
- 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
- 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
- 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
使用场景
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
- 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
- 一个对象必须通知其他对象,而并不知道这些对象是谁。
- 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。