设计模式-观察者模式

简介:

观察者模式分析

观察者模式又叫做发布-订阅(Publish/Subscribe)模式。
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

特点:
观察者模式的通知者可以有任意数目的依赖它的Observer,一旦通知者的状态改变,所有的Observer都可以得到通知。通知者发出通知时并不需要知道谁是它的观察者,也就是说,具体观察者是谁,它根本不需要知道。
总得来讲,观察者模式所做的工作其实就是在接触耦合。让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。

那么在观察者模式时,如果观察者之间非常相似,那么可以用抽象类来共用一些代码,但是现实编程中,具体的观察者完全有可能是风马牛不相及的类,但它们都需要根据通知者的通知来做出Update()操作,所以让它们都实现下面一个接口就可以实现这个想法了。

interface Observer {
    void update();
}

下面这个观察者模式,我会一步步以例子进行分析。
下面的观察者以工作中开小差玩游戏的同事为原型,通知者以秘书为原型,一旦老板出现,秘书立即通知玩游戏、睡觉的同事,做好准备。

双向耦合代码

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
package observer1;

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

/**
* Created by benjamin on 1/4/16.
* 秘书类
*/

public class Secretary {
private List<PlayGameObserver> observers = new ArrayList<PlayGameObserver>(); //观察者的集合
private String noticeMessage; //通知的消息

public Secretary(){}

/**
* 增加观察者
* @param observer
*/

public void addObserver(PlayGameObserver observer) {
observers.add(observer);
}

/**
* 删除观察者
* @param observer
*/

public void removeObserver(PlayGameObserver observer) {
observers.remove(observer);
}

/**
* 通知所有观察者
*/

public void notifyObserver() {
for (PlayGameObserver o : observers) {
o.update();
}
}

public String getNoticeMessage() {
return noticeMessage;
}

public void setNoticeMessage(String noticeMessage) {
this.noticeMessage = noticeMessage;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package observer1;

/**
* Created by benjamin on 1/4/16.
* 玩游戏的观察者
*/

public class PlayGameObserver {

private String name; //玩游戏的观察者姓名
private Secretary sub; //通知者(秘书)

public PlayGameObserver(String name, Secretary sub) {
this.name = name;
this.sub = sub;
}

public void update() {
System.out.println(sub.getNoticeMessage() + this.name + "关闭游戏,继续工作!");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package observer1;

/**
* Created by benjamin on 1/4/16.
*/

public class ObserverTest {

public static void main(String[] args) {
Secretary secretary = new Secretary();
PlayGameObserver o1 = new PlayGameObserver("王菲", secretary);
secretary.addObserver(o1);

secretary.setNoticeMessage("老板回来了!");
secretary.notifyObserver();
}
}

结果输出为:

老板回来了!王菲关闭游戏,继续工作!

上面的类虽然能实现功能,但是仔细一看,他们之间的耦合太大了。秘书类需要增加观察者,而观察者们需要秘书类。如果观察者不止一个人,并且干得事情不一样的时候(比如睡觉、吃东西什么的),就无法让秘书增加对应的观察者,并一起通知。我们来改改代码。

解耦

抽象观察者类(Observer)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package observer1;

/**
* Created by benjamin on 1/4/16.
* 观察者
*/

public abstract class Observer {

protected String name;
protected Notification notice;

public Observer(String name, Notification notice) {
this.name = name;
this.notice = notice;
}

public abstract void update(); //更新抽象类
}

抽象通知者类(Notification)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package observer1;

/**
* Created by benjamin on 1/4/16.
*/

public abstract class Notification {

protected String noticeMessage; //通知消息

public abstract void addObserver(Observer observer);
public abstract void removeObserver(Observer observer);
public abstract void notifyObserver();

public String getNoticeMessage() {
return noticeMessage;
}

public void setNoticeMessage(String noticeMessage) {
this.noticeMessage = noticeMessage;
}
}

观察者们

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package observer1;

/**
* Created by benjamin on 1/4/16.
* 玩游戏的观察者
*/

public class PlayGameObserver extends Observer {

public PlayGameObserver(String name, Notification notice) {
super(name, notice);
}

@Override
public void update() {
System.out.println(notice.noticeMessage + name + "关闭游戏,继续工作!");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package observer1;

/**
* Created by benjamin on 1/4/16.
* 睡觉的观察者
*/

public class SleepObserver extends Observer {

public SleepObserver(String name, Notification notice) {
super(name, notice);
}

@Override
public void update() {
System.out.println(notice.noticeMessage + name + "停止睡觉,继续工作!");
}
}

通知者们

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
package observer1;

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

/**
* Created by benjamin on 1/4/16.
* 秘书类
*/

public class Secretary extends Notification {
private List<Observer> observers = new ArrayList<Observer>(); //观察者集合

@Override
public void addObserver(Observer observer) {
observers.add(observer);
}

@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}

@Override
public void notifyObserver() {
for (Observer o : observers) {
o.update();
}
}
}
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
package observer1;

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

/**
* Created by benjamin on 1/4/16.
* 老板
*/

public class Boss extends Notification {
private List<Observer> observers = new ArrayList<Observer>(); //观察者集合

@Override
public void addObserver(Observer observer) {
observers.add(observer);
}

@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}

@Override
public void notifyObserver() {
for (Observer o : observers) {
o.update();
}
}
}

进行测试:

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
package observer1;

/**
* Created by benjamin on 1/4/16.
*/

public class ObserverTest {

public static void main(String[] args) {
// 第一次老板回来了,秘书通知
Secretary secretary = new Secretary();
PlayGameObserver o1 = new PlayGameObserver("王菲", secretary);
SleepObserver o2 = new SleepObserver("谢霆锋", secretary);
secretary.addObserver(o1);
secretary.addObserver(o2);

secretary.setNoticeMessage("老板回来了!");
secretary.notifyObserver();

System.out.println("------------------------------------");

// 第二次老板回来了,找秘书有事,秘书来不及通知,老板看见了,老板来通知
Boss huhansan = new Boss();
PlayGameObserver p1 = new PlayGameObserver("张柏芝", huhansan);
PlayGameObserver p2 = new PlayGameObserver("谢霆锋", huhansan);
SleepObserver p3 = new SleepObserver("王菲", huhansan);
huhansan.addObserver(p1);
huhansan.addObserver(p2);
huhansan.addObserver(p3);
huhansan.removeObserver(p3); //抓到一个

huhansan.setNoticeMessage("你们不好好工作!");
huhansan.notifyObserver();

/**
* 输出
* 老板回来了!王菲关闭游戏,继续工作!
老板回来了!谢霆锋停止睡觉,继续工作!
------------------------------------
你们不好好工作!张柏芝关闭游戏,继续工作!
你们不好好工作!谢霆锋关闭游戏,继续工作!
*/

}
}

观察者模式的不足

比如我们在使用Eclipse以及Xcode等一些编辑工具的时候,我们可能一个运行可以导致很多窗口隐藏或者出现,而且各个变化都涉及到不同的控件。那么我们要是想通知它们,不可能让它们都去实现Observer接口,因为这些控件早都已经被他们的制造商给封装了。

尽管我们用了依赖倒转原则,但是‘抽象通知者’还是依赖‘抽象观察者’,也就是说,万一没有了抽象观察者这样的接口,我这通知的功能就完不成了。另外就是每个具体观察者,它不一定要使用‘更新’这个方法,有可能是‘工具箱隐藏’方法,有可能是‘导航栏变色’等。这根本就是不同名的方法,这应该就是不足的地方吧。

运用事件委托delegate机制

委托就是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值。委托可以看做是对函数的抽象,是函数的“类”,委托的实例将代表一个具体的函数。
一个委托可以搭载多个方法,所有方法被依次唤起。更重要的是,它可以使得委托对象所搭载的方法并不需要属于同一个类。

刚刚的例子我们加上Event和EventHandler作为委托,修改代码如下:

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
package observer1;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
* Created by benjamin on 1/4/16.
*/

public class Event {

private Object object;
private String methodName;
private String[] params;
private Class[] paramTypes;

public Event(Object object, String methodName, String...params){
this.object = object;
this.methodName = methodName;
this.params = params;
resolveParams(this.params);
}

private void resolveParams(String[] params) {
int length = params.length;
paramTypes = new Class[length];
for (int i = 0; i < length; i ++) {
paramTypes[i] = params[i].getClass();
}
}

public void invoke() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Method method = object.getClass().getMethod(methodName, paramTypes);
if (method == null) return;
method.invoke(object, params);
}

}
1
2
3
4
5
6
7
8
9
10
package observer1;

/**
* Created by benjamin on 1/4/16.
*/

public interface Delegate {

void addEvent(Event e);
void notifyX() throws Exception;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package observer1;

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

/**
* Created by benjamin on 1/4/16.
*/

public class EventHandler implements Delegate{
private List<Event> events = new ArrayList<Event>();

public EventHandler(){}

public void addEvent(Event e) {
events.add(e);
}

public void notifyX() throws Exception {
for (Event e : events) {
e.invoke();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package observer1;

/**
* Created by benjamin on 1/4/16.
*/

public abstract class Notification {

private Delegate delegate ;

public Notification(Delegate delegate) {
this.delegate = delegate;
}

public abstract void addListener(Object object, String methodName, String...params);
public abstract void notifyX();

public Delegate getDelegate() {
return this.delegate;
}
}
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
package observer1;

/**
* Created by benjamin on 1/4/16.
* 秘书类
*/

public class Secretary extends Notification {

public Secretary(Delegate delegate) {
super(delegate);
}

@Override
public void addListener(Object object, String methodName, String... params) {
this.getDelegate().addEvent(new Event(object, methodName, params));
}

@Override
public void notifyX() {
try {
this.getDelegate().notifyX();
} catch (Exception e) {
e.printStackTrace();
}
}
}
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
package observer1;

/**
* Created by benjamin on 1/4/16.
* 老板
*/

public class Boss extends Notification {

public Boss(Delegate delegate) {
super(delegate);
}

@Override
public void addListener(Object object, String methodName, String... params) {
this.getDelegate().addEvent(new Event(object, methodName, params));
}

@Override
public void notifyX() {
try {
this.getDelegate().notifyX();
} catch (Exception e) {
e.printStackTrace();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package observer1;

/**
* Created by benjamin on 1/4/16.
* 玩游戏的观察者
*/

public class PlayGameObserver {

private String gamer;

public PlayGameObserver(String name) {
gamer = name;
}

public void stopGame(String noticeMessage) {
System.out.println(noticeMessage + gamer + "关闭游戏,继续工作!");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package observer1;

/**
* Created by benjamin on 1/4/16.
* 睡觉的观察者
*/

public class SleepObserver {

private String sleeper;

public SleepObserver(String name) {
sleeper = name;
}

public void stopSleep(String noticeMessage) {
System.out.println(noticeMessage + sleeper + "停止睡觉,继续工作!");
}
}
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
package observer1;

import sun.jvm.hotspot.runtime.Threads;

/**
* Created by benjamin on 1/4/16.
*/

public class ObserverTest {

public static void main(String[] args) {
// 第一次老板回来了,秘书通知
Delegate delegate = new EventHandler();
Secretary secretary = new Secretary(delegate);
PlayGameObserver o1 = new PlayGameObserver("王菲");
SleepObserver o2 = new SleepObserver("谢霆锋");
secretary.addListener(o1, "stopGame", "老板来了!");
secretary.addListener(o2, "stopSleep", "老板来了!");

try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
secretary.notifyX();

System.out.println("------------------------------------");

// 第二次老板回来了,找秘书有事,秘书来不及通知,老板看见了,老板来通知
Delegate delegate1 = new EventHandler();
Boss huhansan = new Boss(delegate1);
PlayGameObserver p1 = new PlayGameObserver("张柏芝");
PlayGameObserver p2 = new PlayGameObserver("谢霆锋");
SleepObserver p3 = new SleepObserver("王菲");
huhansan.addListener(p1, "stopGame", "你们不好好工作!");
huhansan.addListener(p2, "stopGame", "你们不好好工作!");
huhansan.addListener(p3, "stopSleep", "你们不好好工作!");

try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
huhansan.notifyX();

/**
* 输出
* 老板回来了!王菲关闭游戏,继续工作!
老板回来了!谢霆锋停止睡觉,继续工作!
------------------------------------
你们不好好工作!张柏芝关闭游戏,继续工作!
你们不好好工作!谢霆锋关闭游戏,继续工作!
你们不好好工作!王菲停止睡觉,继续工作!
*/

}
}

最后总结:
1、通知者不知道玩游戏的和睡觉的存在,完全解耦。(功劳归功于Event和EventHandler)
2、老板来后,一次通知,并且通知的消息可以变化,执行不同的方法
3、扩展性很高,再来一个玩纸牌的加上就可以,告诉一下通知者。

目录
相关文章
|
4月前
|
设计模式 PHP
php设计模式--观察者模式(4.1)面向过程完成页面内容切换
php设计模式--观察者模式(4.1)面向过程完成页面内容切换
30 0
|
6天前
|
设计模式 Java 关系型数据库
设计模式——观察者模式
观察者模式介绍、观察者模式优化天气预报案例、JDK 的Observable类和Observer类
设计模式——观察者模式
|
4月前
|
设计模式 监控 Java
设计模式 - 观察者模式(Observer):Java中的战术与策略
【4月更文挑战第7天】观察者模式是构建可维护、可扩展系统的关键,它在Java中通过`Observable`和`Observer`实现对象间一对多的依赖关系,常用于事件处理、数据绑定和同步。该模式支持事件驱动架构、数据同步和实时系统,但需注意避免循环依赖、控制通知粒度,并关注性能和内存泄漏问题。通过明确角色、使用抽象和管理观察者注册,可最大化其效果。
99 2
|
1月前
|
设计模式 存储 前端开发
【十四】设计模式~~~行为型模式~~~观察者模式(Java)
文章详细介绍了观察者模式(Observer Pattern),这是一种对象行为型模式,用于建立对象之间的一对多依赖关系。当一个对象状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。文中通过交通信号灯与汽车的案例以及多人联机对战游戏的设计方案,阐述了观察者模式的动机和应用场景。接着,文章介绍了观察者模式的结构、角色、优点、缺点以及适用情况,并通过代码示例展示了如何在Java中实现观察者模式。此外,还探讨了观察者模式在MVC架构中的应用以及Java中对观察者模式的支持。
【十四】设计模式~~~行为型模式~~~观察者模式(Java)
|
1月前
|
设计模式 安全 Go
[设计模式]行为型模式-观察者模式
[设计模式]行为型模式-观察者模式
|
1月前
|
设计模式 Go
go 设计模式之观察者模式
go 设计模式之观察者模式
|
2月前
|
设计模式 安全 Java
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
60 1
|
2月前
|
设计模式 缓存
iLogtail设计模式问题之观察者模式在iLogtail中是如何应用的
iLogtail设计模式问题之观察者模式在iLogtail中是如何应用的
|
3月前
|
设计模式 消息中间件 存储
跟着GPT学设计模式之观察者模式
观察者模式是一种行为型设计模式,它定义了对象之间的一对多依赖关系,使得当一个对象的状态发生改变时,其依赖对象都能够收到通知并自动更新。一般情况下,被依赖的对象叫作被观察者(Observable),依赖的对象叫作观察者(Observer)。
26 1
|
3月前
|
设计模式 存储 Java
Java设计模式之观察者模式详解
Java设计模式之观察者模式详解