👉文章示例代码👈
定义
定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者(观察者)都会收到通知并且自动更新。
观察者模式提供了一种对象设计,让观察者和被观察者(主题)之间松耦合。
四个角色
观察者模式的主要角色有以下四个:
- 抽象主题角色Subject:将所欲对观察者对象的引用保存在一个集合里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加或者删除观察者对象。
- 具体主题角色Concrete Subject:将有关状态存入具体观察者对象。在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
- 抽象观察者角色Observer:给所有的具体观察者定义一个接口,在得到主题的通知时更新自己。
- 具体观察者角色Concrete Observer:实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相互协调。
场景示例
最近新冠肆虐,药店的口罩很是畅销,甚至是脱销,大家都等着药店进口罩的通知。笔者以此作为示例。
方便起见,笔者这里使用Java内置的观察者模式。
在 java.util
包当中包含了最基本的 Observer
接口和 Observable
类,这与我们的抽象主题 Subject
和抽象观察者 Observer
很是相似。而且由于 Observer
接口和 Observable
类已经预先实现了许多功能,使得我们在使用上更加的方便。
创建具体主题
这里方法中调用的 setChanged()
方法和 notifyObservers()
方法都是由 Observable
类提供的。 setChanged()
代表着主题状态的改变。 notifyObservers()
则表示通知所有的注册了的观察者,参数可传可不传。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
public class Pharmacy extends Observable {
private String name;
public Pharmacy(String name) { this.name = name; }
public String getName() { return name; }
public void purchase(Pharmacy pharmacy, GauzeMask gauzeMask) { System.out.println(String.format("%s最近刚采购了一批%s, 数量为%s, 单价为%s", pharmacy.getName(), gauzeMask.getType(), gauzeMask.getAmount(), gauzeMask.getPrice()));
setChanged();
notifyObservers(gauzeMask); } }
|
创建具体观察者
update()
是父类抽象观察者 Obserber
中定义的方法。参数 Observable o
代表的是被观察的对象, Object arg
表示的是主题中发布通知时传递的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
public class Customer implements Observer {
private String name;
public Customer(String name) { this.name = name; }
public void update(Observable o, Object arg) { Pharmacy pharmacy = (Pharmacy) o; GauzeMask gauzeMask = (GauzeMask) arg; System.out.println(String.format("顾客%s收到%s的通知: 最近刚采购了一批%s, 数量为%s, 单价为%s", this.name, pharmacy.getName(), gauzeMask.getType(), gauzeMask.getAmount(), gauzeMask.getPrice())); } }
|
创建缺省实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
public class GauzeMask {
private String type;
private int amount;
private double price;
public GauzeMask(String type, int amount, double price) { this.type = type; this.amount = amount; this.price = price; }
public String getType() { return type; }
public int getAmount() { return amount; }
public double getPrice() { return price; } }
|
测试类及输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
public class Test {
public static void main(String[] args) { Pharmacy pharmacy = new Pharmacy("一方大药房"); GauzeMask gauzeMask = new GauzeMask("一次性口罩", 5000, 4.8);
Customer customer1 = new Customer("海豪"); Customer customer2 = new Customer("亚萍"); pharmacy.addObserver(customer1); pharmacy.addObserver(customer2);
pharmacy.purchase(pharmacy, gauzeMask); } }
|
测试类的输出结果如下:
一方大药房最近刚采购了一批一次性口罩, 数量为5000, 单价为4.8
顾客亚萍收到一方大药房的通知: 最近刚采购了一批一次性口罩, 数量为5000, 单价为4.8
顾客海豪收到一方大药房的通知: 最近刚采购了一批一次性口罩, 数量为5000, 单价为4.8
类结构图
以上示例类的结构图如下所示

总结
适用场景
对象之间存在一对多的关系,同时一个对象的状态发生改变会影响其他对象。
优点
- 观察者和被观察者之间是抽象耦合关系,降低了两者的耦合度。
- 观察者与被观察者之间建立了一套触发机制。
缺点
- 有可能会出现循环引用。
- 当观察者对象很多时,发布通知的时间消耗会延长,从而影响程序的效率。
参考