GeekIBLi

设计模式之美-观察者模式

2021-08-07

观察者模式

观察者模式

指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。

属于行为型模式

类图

ISubject

1
2
3
4
5
6
7
8
public interface ISubject<E> {
boolean attach(IObserver<E> observer);

boolean detach(IObserver<E> observer);

void notify(E event);
}

ConcreteSubject

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
public class ConcreteSubject<E> implements ISubject<E> {

private List<IObserver<E>> observers = new ArrayList<>();

@Override
public boolean attach(IObserver<E> observer) {
if (this.observers.contains(observer)) {
return false;
}
observers.add(observer);
return true;
}

@Override
public boolean detach(IObserver<E> observer) {
return this.observers.remove(observer);
}

@Override
public void notify(E event) {
for (IObserver observer : observers) {
observer.update(event);
}
}
}

IObserver

1
2
3
4
public interface  IObserver<E> {
void update(E event);
}

ConcreteObserver

1
2
3
4
5
6
public class ConcreteObserver<E> implements IObserver<E> {
@Override
public void update(E event) {
System.err.println("receive event " + event);
}
}

DemoTest

1
2
3
4
5
6
7
8
9
10
public class DemoTest {
public static void main(String[] args) {
ISubject<String> subject = new ConcreteSubject<String>();
subject.attach(new ConcreteObserver<String>());
subject.attach(new ConcreteObserver<String>());
subject.attach(new ConcreteObserver<String>());
subject.notify("test");
}
}

JDK实现观察者模式

比如有一个博客系统提供了问题社区,一个人提出问题,会有其他人收到这个消息;我们使用JDK来模拟一个简单的场景;

Blog 被观察对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Blog extends Observable {

private Blog() {
}

private static final Blog blog = new Blog();

private String name = "IBLi Blog";

public static Blog getInstance() {
return blog;
}

public void question(Question question) {
System.out.println(question.getUserName() + " 在 blog 提交了问题 : " + question.getContent());
setChanged();
notifyObservers(question);
}
}

Question

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
public class Question {

private String userName;
private String content;

public Question(String userName, String content) {
this.userName = userName;
this.content = content;
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}
}

Student 学生类 被通知对象

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
public class Student implements Observer {

private String name;

public Student(String name) {
this.name = name;
}

public String getName() {
return name;
}

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

/**
* This method is called whenever the observed object is changed. An
* application calls an <tt>Observable</tt> object's
* <code>notifyObservers</code> method to have all the object's
* observers notified of the change.
*
* @param o the observable object.
* @param arg an argument passed to the <code>notifyObservers</code>
*/
@Override
public void update(Observable o, Object arg) {
Blog blog = (Blog) o;
Question question = (Question) arg;
System.out.println("******************");
System.out.println(this.name + " 看到了 " + question.getUserName() + " 的问题 \n" + "问题是: " + question.getContent());
}
}

Client 测试

1
2
3
4
5
6
7
8
9
10
11
public class Client {
public static void main(String[] args) {
Blog blog = Blog.getInstance();

blog.addObserver(new Student("Tom"));
blog.addObserver(new Student("John"));
Question question = new Question("小明","观察者模式是什么?");
blog.question(question);
}
}

测试结果

1
2
3
4
5
6
7
小明 在 blog 提交了问题 : 观察者模式是什么?
******************
John看到了 小明 的问题
问题是: 观察者模式是什么?
******************
Tom看到了 小明 的问题
问题是: 观察者模式是什么?

基于Guava实现观察者模式

Student 被观察对象

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
public class Student {
private String name;

public Student(String name) {
this.name = name;
}

public String getName() {
return name;
}

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

@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}

@Subscribe
public void Observer(Student str) {
System.err.println("student invoke method + " + str.toString());
}
}

Teacher 被通知对象

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
public class Teacher {

private String name;

public Teacher(String name) {
this.name = name;
}

public String getName() {
return name;
}

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

@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
'}';
}

@Subscribe
public void Observer(Teacher str) {
System.err.println("teacher invoke method + " + str.toString());
}
}

Client 测试类

1
2
3
4
5
6
7
8
9
10
11
12
public class Client {

public static void main(String[] args) {
EventBus eventBus = new EventBus();
Student student = new Student("Ibli");
Teacher teacher = new Teacher("laoshi");
eventBus.register(student);
eventBus.register(teacher);
eventBus.post(new Student("xuesheng"));
eventBus.post(new Teacher("teacher"));
}
}

测试结果

1
2
student invoke method + Student{name='xuesheng'}
teacher invoke method + Teacher{name='teacher'}

观察者模式使用场景

1、当一个抽象模型包含两个方面内容,其中一个方面依赖另一个方面
2、其他一个或多个对象的变化依赖另一个对象的变化
3、实现类似广播机制的功能,无需知道具体收听者,只需要广播。系统中感兴趣的对象会自动接口该广播
4、多层级嵌套机制,形成一种链式出发机制,是的时间具备跨域(跨越两种观察者类型)通知

观察者模式优点✅

1、降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。
2、目标与观察者之间建立了一套触发机制。

观察者模式缺点

目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。