三、设计模式学习
2、观察者模式(Observer Pattern)
2.1 简介
观察者模式是一种对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
2.2 模式结构
- Subject: 目标
- ConcreteSubject: 具体目标
- Observer: 观察者
- ConcreteObserver: 具体观察者
2.3时序图
2.4 模拟实现
Subject: 目标
/**
* 被观察者
* [@author](https://my.oschina.net/arthor) Lenovo
*
* [@param](https://my.oschina.net/u/2303379) <T>
*/
public interface Subject<T> {
//存储订阅者
List<Observer> list = new ArrayList<>();
//注册订阅者
void registerObserver(T obs);
void getList();
//移除
void removeObserver(T obs);
//通知所有订阅者更新状态
void notifyAllObservers();
}
ConcreteSubject: 具体目标
/**
* 具体被观察者
* [@author](https://my.oschina.net/arthor) Lenovo
*
*/
public class ConcreteSubject implements Subject<Observer> {
private int status;
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
this.notifyAllObservers();
}
[@Override](https://my.oschina.net/u/1162528)
public void registerObserver(Observer obs) {
list.add(obs);
}
[@Override](https://my.oschina.net/u/1162528)
public void notifyAllObservers() {
list.forEach((t)->{
t.update(this);
});
}
@Override
public void removeObserver(Observer obs) {
System.out.println(list.remove(obs)+"移除");
}
@Override
public void getList() {
list.forEach((t)->System.out.println(t));
}
}
Observer: 观察者
/**
* 订阅者
* @author Lenovo
*
*/
public interface Observer {
void update(Subject subject);
}
ConcreteObserver: 具体观察者
/**
* 具体观察者
* @author Lenovo
*
*/
public class ConcreteObserver implements Observer {
private int myState;
public int getMyState() {
return myState;
}
public void setMyState(int myState) {
this.myState = myState;
}
@Override
public void update(Subject subject) {
myState= ((ConcreteSubject)subject).getStatus();
}
@Override
public String toString() {
return "ConcreteObserver [myState=" + myState + "]";
}
}
测试控制台输出
import org.junit.Test;
public class ObserverTest {
ConcreteSubject subject = new ConcreteSubject();
ConcreteObserver b1 = new ConcreteObserver();
ConcreteObserver b2 = new ConcreteObserver();
@Test
public void test1() {
subject.registerObserver(b1);
subject.registerObserver(b2);
System.out.println("-----------");
subject.setStatus(2);
subject.getList();
System.out.println(b1.getMyState()+"b1");
System.out.println(b2.getMyState()+"b2");
subject.setStatus(300);
subject.getList();
System.out.println(b1.getMyState()+"b1");
System.out.println(b2.getMyState()+"b2");
System.out.println("移除监听b1--------");
subject.removeObserver(b1);
subject.setStatus(3000);
subject.getList();
System.err.println(b1.getMyState()+"b1");
System.out.println(b2.getMyState()+"b2");
}
}
2.5 优点
- 观察者模式可以实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色。
- 观察者模式在观察目标和观察者之间建立一个抽象的耦合。
- 观察者模式支持广播通信。
- 观察者模式符合“开闭原则”的要求。
2.6 缺点
- 如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
- 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
- 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
2.7 redis 应用
注册广播
127.0.0.1:6379> SUBSCRIBE c1 c2
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "c1"
3) (integer) 1
1) "subscribe"
2) "c2"
3) (integer) 2
广播
127.0.0.1:6379> PUBLISH c1 name
(integer) 1
127.0.0.1:6379> PUBLISH c2 alili
(integer) 1
127.0.0.1:6379>
广播后
127.0.0.1:6379> SUBSCRIBE c1 c2
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "c1"
3) (integer) 1
1) "subscribe"
2) "c2"
3) (integer) 2
1) "message"
2) "c1"
3) "name"
1) "message"
2) "c2"
3) "alili"