设计模式5-Observer

本文最后更新于:3 years ago

观察者模式

  当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于 行为型模式

如果想了解观察者模式的具体的介绍,菜鸟教程介绍得比较详细↓
菜鸟教程-观察者模式

结构图


优缺点

优点:
1、观察者和被观察者是抽象耦合的。
2、建立一套触发机制。

缺点:
1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

使用场景

1、一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
2、一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
3、一个对象必须通知其他对象,而并不知道这些对象是谁。
4、需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。

实现代码

提出需求,我们知道一台戏中,当演员出场,此时灯光亮起,音乐响起,旁白响起。
这里我们模拟的就是当演员出场时,观察者(灯光、音乐和旁白)的动作。

代码也不难:

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
41
42
43
44
45
class Acter {
private boolean act = false;
private Light l = new Light();
private Music m = new Music();
private Narrator n = new Narrator();

public boolean isAct() {
return act;
}

//耦合度比较高
public void Acting() {
act = true;
l.turnOn();
m.sound();
n.ring();
}
}

class Light{
public void turnOn(){
System.out.println("turnOn...");
}
}

class Music{
public void sound(){
System.out.println("sound...");
}
}

class Narrator{
public void ring(){
System.out.println("ring...");
}
}

public class Act {
public static void main(String[] args) {
Acter acter = new Acter();
System.out.println(acter.isAct());
acter.Acting();
System.out.println(acter.isAct());
}
}

这里我们创建Light、Music和Narrator三个对象,将这三个对象定义在Acter对象中,当调用Acting()方法时,将act属性设为true,
灯亮、音乐和旁白响起。

我们在代码中也指出了弊端,同时将Light、Music和Narrator三个对象都放在了Acting中处理了,耦合度太高了。
再结合实际来讲,Music响起,不一定就是演员登场了,可能是某个转折点,观察者的动作不一定要耦合到被观察者身上。

那么此时我们要怎么解决呢?仔细分析逻辑会发现,只要当演员出场时,不管是灯、音乐还是旁白都会有相应的动作,那么我们就可以创建一个接口,然后观察者来实现我们这个接口,从而实现分离观察者和被观察者

代码如下:

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
class Acter{
private boolean act = false;
List<Observer> observers = new ArrayList<Observer>();
{
//这里可以通过配置文件实现
observers.add(new Light());
observers.add(new Music());
observers.add(new Narrator());
}

public boolean isAct(){
return act;
}

public void Acting(){
act = true;
for (Observer o : observers){
o.actionOnActing();
}
}
}

interface Observer{
void actionOnActing();
}

class Light implements Observer{

public void turnOn(){
System.out.println("turnOn...");
}

@Override
public void actionOnActing() {
turnOn();
}
}

class Music implements Observer{
public void sound(){
System.out.println("sound...");
}


@Override
public void actionOnActing() {
sound();
}
}

class Narrator implements Observer{
public void ring(){
System.out.println("ring...");
}

@Override
public void actionOnActing() {
ring();
}
}

public class Act {
public static void main(String[] args) {
Acter acter = new Acter();
System.out.println(acter.isAct());
acter.Acting();
System.out.println(acter.isAct());
}
}

接着,需求升级,我们知道当演员在出场时,观察者会有所动作。但根据实际情况分析,演员也是不同的,每个演员出场的音乐、灯光、和旁白都是不一样的。那这个时候,我们需要怎么办,要传参数了。

我们创建一个事件类,在调用观察者中的方法时,需要传入这个事件类,此时即可满足需求。

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
class Acter{
private boolean act = false;
List<Observer> observers = new ArrayList<Observer>();
{
observers.add(new Light());
observers.add(new Music());
observers.add(new Narrator());
}

public boolean isAct(){
return act;
}

actionEvent event = new actionEvent("男", "role1");

public void action(){
act = true;
for (Observer o : observers){
System.out.println(event.gender);
o.actionOnActing(event);
}
}
}

// 事件类
class actionEvent{
String gender;
String roleName;

public actionEvent(String gender, String roleName) {
this.gender = gender;
this.roleName = roleName;
}
}

interface Observer{
void actionOnActing(actionEvent event);
}

class Light implements Observer{

public void turnOn(){
System.out.println("TurnOn...");
}
public void turnOnBg1(){
System.out.println("turnOnBg1...");
}

@Override
public void actionOnActing(actionEvent event) {
if (event.roleName == "role1") turnOn();
else if (event.roleName != "role1") turnOnBg1();
}
}

class Music implements Observer{
public void sound(){
System.out.println("Sound...");
}


@Override
public void actionOnActing(actionEvent event) {
sound();
}
}

class Narrator implements Observer{
public void ring(){
System.out.println("Ring...");
}

@Override
public void actionOnActing(actionEvent event) {
ring();
}
}

public class Main {
public static void main(String[] args) {
Acter acter = new Acter();
System.out.println(acter.isAct());
acter.action();
System.out.println(acter.isAct());
}
}

但是其实在很多时候,观察者需要根据事件的具体情况来进行处理。大多数时候,我们处理事件的时候,需要事件源对象。观察者作出的反应,和事件源本身并不是完全耦合的紧密。

有人可能会说,当你new一个Observer把事件源传进去不就行了吗?
答案是不行的,此时就相当于将Observer和具体的事件源对象绑定了,那么就只能观察这一个事件源对象了
我们将actionEvent接口中,添加一个属性,当我们需要它的源对象的时候,getSource()即可
(这里微微感叹一下,这些人真聪明)

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
class Acter{
private boolean act = false;
List<Observer> observers = new ArrayList<Observer>();
{
observers.add(new Light());
observers.add(new Music());
observers.add(new Narrator());
}

public boolean isAct(){
return act;
}

actionEvent event = new actionEvent("男", "role1", this);

public void action(){
act = true;
for (Observer o : observers){
System.out.println(event.gender);
o.actionOnActing(event);
}
}
}

// 事件类
class actionEvent{
String gender;
String roleName;
Acter source;

public actionEvent(String gender, String roleName, Acter source) {
this.gender = gender;
this.roleName = roleName;
this.source = acter;
}

public Acter getSource() {
return source;
}
}

interface Observer{
void actionOnActing(actionEvent event);
}

class Light implements Observer{

public void turnOn(){
System.out.println("TurnOn...");
}
public void turnOnBg1(){
System.out.println("turnOnBg1...");
}

@Override
public void actionOnActing(actionEvent event) {
if (event.roleName == "role1") turnOn();
else if (event.roleName != "role1") turnOnBg1();
}
}

class Music implements Observer{
public void sound(){
System.out.println("Sound...");
}


@Override
public void actionOnActing(actionEvent event) {
sound();
}
}

class Narrator implements Observer{
public void ring(){
System.out.println("Ring...");
}

@Override
public void actionOnActing(actionEvent event) {
ring();
}
}

public class Main {
public static void main(String[] args) {
Acter acter = new Acter();
System.out.println(acter.isAct());
acter.action();
System.out.println(acter.isAct());
}
}

到这里大致就差不多说清楚了,观察者模式比较重要,所以这里🔒得比较仔细。

最后再说一下
事件也可以形成体系,事件本身可以有很多很多的类型(通过implements、extends),这就是体系。
拿上面的代码来讲,可以将 actionEvent 继承抽象类 Event ,抽象类中含有 getSource() 方法,此时 actionEvent 中就继承了此方法(形成体系,这里只是简单模拟一下)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
abstract class Event<T>{
abstract T getSource();
}

// 事件类
class actionEvent extends Event<Acter>{
String gender;
String roleName;
Acter source;

public actionEvent(String gender, String roleName, Acter source) {
this.gender = gender;
this.roleName = roleName;
this.source = acter;
}

public Acter getSource() {
return source;
}
}

在很多系统中,Observer模式往往和责任链共同负责对于事件的处理,其中的某一个observer负责是否将事件进一步传递。
下一篇介绍一下责任链模式


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!