1.解析

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。

观察者模式(有时又被称为发布(publish )-订阅(Subscribe)模式、模型-视图(View)模式、源-收听者(Listener)模式或从属者模式)

UML :


设计模式之观察者模式(Observer)_观察者模式

分类 :
(1)拉模式 :观察者通过目标对象,自己调用要获取的信息。
(2)推模式 : 当目标对象进行消息通知的时候,观察者直接就可以收到信息。

基本实现步骤:
(1)实现观察者接口,进行消息接收
(2)实现目标接口,进行消息通知
(3)实现观察者,就是要接收消息的子类
(4)实现目标者,进行用户存储,循环通知观察者


2.实例

背景:
接着上一篇,饭店经过装修后,发现收益不大,仅仅收支平衡,故,需要考虑营业模式了:
(1)针对用户进行一些思考,发现用户有小孩子,程序员,学生等。
(2)针对不同的用户群体,制作不同的营养套餐并推荐.普通 :大众套装,小孩:营养套餐,程序员: 壮阳补肾套餐;
(3)开启vip 功能,不同级别的vip 收到的折扣不一样.初级会员 9.5折,中级会员 8.5折,高级会员 7.0折,白银会员6.0折,黄金会员 5.0折;

1.自定义观察者模式实现:拉模式和推模式实现

(1)实现Observer 接口 : 实现消息接收

public interface Observer {

/**
* 拉模式 实现更新 : 自己到目标对象拿数据
* @param subject 目标对象
*/
void update(Subject subject);

/**
* 推模式实现 : 直接传递参数 ,到观察者
* @param msg 推送的消息
*/
void update(String msg);

}

(2)实现Subject 接口 : 实现 目标接口,进行消息通知

public interface  Subject {

/**
* 消息通知msg
*/
void notifiy(String msg);

}

黄金会员服务

(3)实现 黄金会员 用户 :观察者对象

package observers;

import observer.Observer;
import subject.Subject;
import subjects.HuangJinUserSubject;

public class HuangJinUserObserver implements Observer {

/**
* 角色:用户
* 任务:用户vip等级
* 目的: 通过拉模式去客户端得到消息
*/

private String name;
private String vip;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getVip() {
return vip;
}

public void setVip(String vip) {
this.vip = vip;
}


@Override
public void update(Subject subject) {
//收到通知消息 : 拉模式实现
System.out.println("亲爱的"+name+" , "+vip+" 会员 ,通知你一下 :"+((HuangJinUserSubject)subject).getMsg());
}

@Override
public void update(String msg) {
// 不使用推模式 : 直接使用 msg消息,不需要去 目标对象去取了
}

}

(4)实现黄金会员目标对象 :作为客户端,给所有的黄金会员进行消息通知

package subjects;

import java.util.ArrayList;
import java.util.List;

import observers.HuangJinUserObserver;
import subject.Subject;


/**
*角色 :大厅经理
*任务:发消息通知
*目的: 服务类,为所有observer ,发生update 消息
*
*/
public class HuangJinUserSubject implements Subject{

/**
* 角色: 电脑
* 任务:为用户的vip服务
* 目的:
*/
private String msg="没有消息";

private List<HuangJinUserObserver> users=new ArrayList<HuangJinUserObserver>();

//添加
public void attach(HuangJinUserObserver u){
if(!users.contains(u)){
users.add(u);
}
}

//移除
public void detach(HuangJinUserObserver u){
if(users.contains(u)){
users.remove(u);
}
}

public String getMsg(){
return msg;
}

//循环通知所有用户
@Override
public void notifiy(String msg) {
this.msg=msg;
for(HuangJinUserObserver u : users){
u.update(this);
}
}
}

普通用户服务

(5)普通用户观察者实现

package observers;

import observer.Observer;
import subject.Subject;

public class PuTongUserObserver implements Observer {

private String name;
private int age;



public int getAge() {
return age;
}


public void setAge(int age) {
this.age = age;
}


public String getName() {
return name;
}


public void setName(String name) {
this.name = name;
}


@Override
public void update(Subject subject) {
//拉模式,这里不使用
}


@Override
public void update(String msg) {
// 推模式,使用这个
if(age<=15){
System.out.println("亲爱的小朋友 : "+name+" , "+msg);
}else if(age>15&&age<=36){
System.out.println("亲爱的美女/帅哥 : "+name+" , "+msg);
}else if(age>36&&age<=60){
System.out.println("亲爱的大叔/大伯: "+name+" , "+msg);
}else{
System.out.println("敬爱的爷爷/奶奶 : "+name+" , "+msg);
}
}

}

(6)普通用户目标对象实现

package subjects;

import java.util.ArrayList;
import java.util.List;

import observers.PuTongUserObserver;
import subject.Subject;

/**
* 角色: 普通会员客户端
*
*/
public class PuTongUserSubject implements Subject{

List<PuTongUserObserver> users=new ArrayList<PuTongUserObserver>();

public void attachUser(PuTongUserObserver u){
if(!users.contains(u)){
users.add(u);
}
}

public void dettachUser(PuTongUserObserver u){
if(users.contains(u)){
users.remove(u);
}
}

@Override
public void notifiy(String msg) {
for(PuTongUserObserver userObserver : users){
userObserver.update(msg);
}
}

}

(7)测试 :

基本测试步骤 : 声明用户对象 -> 声明目标对象 -> 注册用户 -> 消息通知

public static void main(String[] args) {

System.out.println("----------------------------------拉模式------------------");

//1.用户
HuangJinUserObserver user1=new HuangJinUserObserver();
user1.setName("原明卓");
user1.setVip("黄金");

HuangJinUserObserver user2=new HuangJinUserObserver();
user2.setName("小明");
user2.setVip("白银");

//2.声明目标客户端
HuangJinUserSubject userSubject=new HuangJinUserSubject();

//3.注册用户
userSubject.attach(user1);
userSubject.attach(user2);

//4.发送消息
userSubject.notifiy("您的vip 快到期了,快来我店免费办理吧 :)");

System.out.println("----------------------------------推模式------------------");

//1.用户
PuTongUserObserver user3=new PuTongUserObserver();
user3.setAge(10);
user3.setName("小白");

PuTongUserObserver user4=new PuTongUserObserver();
user4.setAge(70);
user4.setName("老白");

//2.声明
PuTongUserSubject puSubject=new PuTongUserSubject();

//3.注册用户
puSubject.attachUser(user3);
puSubject.attachUser(user4);

//4.通知
puSubject.notifiy("我店新推出新套餐,开来品尝,现在品尝打9折哦,办理会员卡,优惠更多哦!!");


}

(8)结果

设计模式之观察者模式(Observer)_观察者模式_02

2.Java API 提供的接口实现

(1)实现观察者 : 实现 java.util.Observer 接口 ,这是官方提供的接口

package observers;

import java.util.Observable;
import java.util.Observer;

import subjects.BaiYinSubject;

public class BaiYinObserver implements Observer {

private String name;
private String vip;

public String getVip() {
return vip;
}

public void setVip(String vip) {
this.vip = vip;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}


@Override
public void update(Observable o, Object arg) {
//推模式使用 Object arg ;
if(arg!=null){
System.out.println("(推模式):亲爱的 "+this.vip+" : "+name+" , "+arg);
}
//拉模式 使用 Observale o ;
if(o!=null){
System.out.println("(拉模式):亲爱的 "+this.vip+" : "+name+" , "+((BaiYinSubject)o).getMsg());
}
}

}

(2)实现目标Subject :Observable 也是 官方提供的

public class BaiYinSubject extends Observable {

List<BaiYinObserver> baiyinUsers=new ArrayList<BaiYinObserver>();
private String msg;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}

@Override
public synchronized void addObserver(Observer o) {
// 添加白银会员
baiyinUsers.add((BaiYinObserver) o);
}

@Override
public synchronized void deleteObserver(Observer o) {
//移除白银会员
baiyinUsers.remove((BaiYinObserver) o);
}

@Override
public void notifyObservers() {
//拉模式:通知所有的白银用户
for(BaiYinObserver by :baiyinUsers){
by.update(this, null);
}
}

@Override
public void notifyObservers(Object arg) {
// 退模式:通知所有的白银会员
for(BaiYinObserver by :baiyinUsers){
by.update(null,arg);
}
}


}

(3)优化目标者,使其可以满足所有的观察者(Observer)子类

观察者将自己注册到被观察者的容器中时,被观察者不应该过问观察者的具体类型,而是应该使用观察者的接口

package subjects;

import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import observers.BaiYinObserver;

public class BaiYinSubject extends Observable {

List<Observer> baiyinUsers=new ArrayList<Observer>();
private String msg;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}

@Override
public synchronized void addObserver(Observer o) {
// 添加白银会员
baiyinUsers.add( o);
}

@Override
public synchronized void deleteObserver(Observer o) {
//移除白银会员
baiyinUsers.remove( o);
}

@Override
public void notifyObservers() {
//拉模式:通知所有的白银用户
for(Observer by :baiyinUsers){
((BaiYinObserver)by).update(this, null);
}
}

@Override
public void notifyObservers(Object arg) {
// 退模式:通知所有的白银会员
for(Observer by :baiyinUsers){
((BaiYinObserver)by).update(null,arg);
}
}

}

(4)测试 :

public static void main(String[] args) {

//用户
BaiYinObserver baiyinUser1=new BaiYinObserver();
baiyinUser1.setName("张银白");
baiyinUser1.setVip("白银会员");

//声明
BaiYinSubject baiYinSubject=new BaiYinSubject();

//注册用户
baiYinSubject.addObserver(baiyinUser1);

//发送通知

//拉通知
baiYinSubject.setMsg("我店最近推出了新品,白金会员优惠更大哦!!!:)");
baiYinSubject.notifyObservers();

//推通知
baiYinSubject.notifyObservers("我勒个去,白金会员优惠老大了!");


}

(5)结果

设计模式之观察者模式(Observer)_subject_03


3.优缺点

优点 :
1、 Subject和Observer之间是松耦合的,分别可以各自独立改变。
2、 Subject在发送广播通知的时候,无须指定具体的Observer,Observer可以自己决定是否要订阅Subject的通知。
3、 遵守大部分GRASP原则和常用设计原则,高内聚、低耦合。

缺点 :
1、 松耦合导致代码关系不明显,有时可能难以理解。(废话)
2、 如果一个Subject被大量Observer订阅的话,在广播通知的时候可能会有效率问题。(毕竟只是简单的遍历)


4.应用场景

(1) 对一个对象状态的更新,需要其他对象同步更新,而且其他对象的数量动态可变。
(2) 对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节。
(3)当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
(4)当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
(5)当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。