观察者模式

在处理类之间的多对一的依赖关系时,观察者设计模式应运而生了,它的出现实现了代码的瘦身,类之间的解耦,本文分三部分:

  • 非观察者模式的多对一的依赖处理
  • 观察者模式对多对一依赖处理的优化
  • Java内置的观察者

假设场景: 前端用户向后端服务器发送不同的请求,后端的Selector区的分不同的请求,回调不同的Handler处理请求, 下面的示例代码

非观察者

当Selector发现数据改变时,想要回调Handler的方法的前提是它要维护一个Handler的引用, 我们在构造函数中传递进去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Data
public class Selector {
private String username;
private String password;

private Handler1 currentConditions;

public Selector(Handler1 currentConditions) {
this.currentConditions = new Handler1();
}

public void datachange() {
currentConditions.update(getUsername(),getPassword());
}

// 添加假数据
public void setData(String username,String password){
this.username=username;
this.password=password;
datachange();
}

}

Handkler 的成员变量和Selector一样, 同时真正干活的人是handler, 它里面只有干活的方法以及承载数据的字段, 干活的方法会被回调, 字段由Selector负责初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Handler1 {
private String username;
private String password;

public void update( String username, String password){
this.username=username;
this.password=password;
display();
}

public void display(){
System.out.println("Handler1 : username"+ username);
System.out.println("Handler1 : password"+username);
}
}

测试:

1
2
3
4
5
6
7
public class text {
public static void main(String[] args) {
Handler1 currentConditions = new Handler1();
Selector weatherData = new Selector(currentConditions);
weatherData.setData("张三","123");
}
}

上面的设计方法实现了,当Selector发现新的数据改变时,回调指定的handler的需求,但是缺点很快就暴露出来, Handler引用以构造方法的形式在Selector维护,如果有100个handler, 是不是意味着Selector的构造函数重载100份?


观察者模式

观察者解决了上面的问题,在观察者模式中有两个角色的划分,1.SubJect 2.Observer, 其中 SubJect负责回调Observer提供的方法处理数据, Observer就是观察者,伺机而动, 它之所以能够实现两者的解耦,实现代码的瘦身,精华就是在Subject的构造函数不再维护单一的某一个Observer的引用,而是一个泛型是为Observer类型的集合, 为了防止把非观察者的对象添加到集合中, 故 在原有的基础上,向上进行了一层抽象,把Subject和Observer设计成接口,Subject提供了注册/移除/通知观察者的方法,通知哪个方法呢? 于是Observer接口中提供了需要被回调的抽象方法, 做完这层封装之后,就有了这个观察者体系, 所有的观察者必须实现Observer接口,重写它的方法,任何回调观察者的类,也必须实现Subject,重写它的方法

实例代码:

1
2
3
4
5
6
7
8
9
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObserver();
}

public interface Observer {
public void update(String m,String p);
}

Subject的实现类:

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
@Data
public class Selector implements Subject {
private String username;
private String password;

private ArrayList<Observer> list;

public Selector() {
this.list = new ArrayList<Observer>();
}

// 添加假数据
public void setData( String username, String password){
this.username=username;
this.password=password;

}

@Override
public void registerObserver(Observer o) {
list.add(o);
}

@Override
public void removeObserver(Observer o) {
list.remove(o);
}

@Override
public void notifyObserver() {
list.forEach(s->s.update(getPassword(),getUsername()));
}
}

Observer的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Handler1 implements Observer {
private String username;
private String password;

@Override
public void update( String username,String password){
this.username=username;
this.password=password;
display();
}

public void display(){
System.out.println(" Handler1 "+ username);
System.out.println(" Handler1 "+password);
}
}

测试:

1
2
3
4
5
6
7
8
9
10
public class text {
public static void main(String[] args) {
Handler1 secondConditions = new Handler1();
Selector weatherData = new Selector();
// 注册观察者
weatherData.registerObserver(secondConditions);
weatherData.setData("张三","123");
weatherData.notifyObserver();
}
}

我们自己实现的观察者函数回调的特性: : 虽然实现我们一开始的需求,但是可以看到,数据是在Subject端进行初始化的, 并没有传递到观察端,由观察者自主分配数据


java内置的观察者

java内置的观察中同样分为两种角色,1.Obserable 2.Observer 其中Observer是观察者,同样不变的是,他是个接口

但是: Obserable是一个抽象类, java为我们实现了 注册/移除,通知的方法, 在不允许多继承的java中,抽象类是有缺点的,这使得当前类不能再继承其他类

此外: 相对于我们自定义的观察者, 它做到了把数据传递到 观察者客户端,任由观察者自主的分配这些数据

如何使用:
对于Obserable来说, 继承他

  • 由于java实现了主要的方法, 我们只关注如何使用这几个方法就行了
  • 1: 在回调之前 , 必须先使用 this.setChanged(); 否则信息不会通知到观察者
  • 2: 通知的方法this.notifyObservers(Object obj));
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
@Data
public class Selector extends Observable {
private String username;
private String password;

// 添加假数据
public void setData( String username, String password) {
this.username = username;
this.password = password;
datachange();
}

// 当信息更改, 就推送
public void datachange() {
// todo 加上这一条,当有信息改变时, 就执行
this.setChanged();
// 通知时,把信息同步推送给观察者
this.notifyObservers(new Data(getUsername(),getPassword()));
}

@lombok.Data
public class Data {
private String username;
private String password;

public Data( String username, String password) {
this.username = username;
this.password = password;
}
}
}

观察者: 继承Observer, 重写它被回调的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Handler1 implements Observer {
private String username;
private String password;

@Override
public void update(Observable observable, Object arg0) {
this.password = ((Selector.Data) (arg0)).getPassword();
this.username = ((Selector.Data) (arg0)).getUsername();
display();
}

public void display() {
System.out.println("Handler1 : " + username);
System.out.println("Handler1 : " + password);
}
}

测试

1
2
3
4
5
6
7
8
9
public class text {
public static void main(String[] args) {
Handler1 handler1 = new Handler1();
Selector weatherData = new Selector();
// 先注册的观察者,后执行
weatherData.addObserver(handler1);
weatherData.setData("张三","123");
}
}