java设计模式- 观察者模式
观察者模式可用于订阅发布类似场景之类使用
- 当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。
介绍
-
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
-
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
-
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
-
如何解决:使用面向对象技术,可以将这种依赖关系弱化。
-
关键代码:在抽象类里有一个 ArrayList 存放观察者们。
-
优点:
- 观察者和被观察者是抽象耦合的。
- 建立一套触发机制。
-
缺点:
- 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
- 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
- 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
-
使用场景:
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
- 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
- 一个对象必须通知其他对象,而并不知道这些对象是谁。
- 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
-
注意事项:
- JAVA 中已经有了对观察者模式的支持类。
- 避免循环引用。
- 如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
代码示例
代码实现:
public interface Observerable {
public void login(Observer user);
public void information();
public void logout(Observer user);
}
public interface Observer {
public void read(String information);
}
public class User implements Observer {
private String name;
@Override
public void read(String information) {
System.out.println(name + "面板: " + information);
}
public User(String name) {
this.name = name;
}
}
public class DnfServer implements Observerable {
private List<Observer> list;
private String information;
public DnfServer() {
list = new ArrayList<>();
}
@Override
public void login(Observer user) {
list.add(user);
}
@Override
public void information() {
for(int i = 0; i < list.size(); i++) {
Observer oserver = list.get(i);
oserver.read(this.information);
}
}
@Override
public void logout(Observer user) {
list.remove(user);
}
public void setInformation(String s) {
this.information = s;
System.out.println("DNF公告: " + s);
//消息更新,通知所有观察者
information();
}
}
测试下
public class Test {
public static void main(String[] args) {
DnfServer server = new DnfServer();
Observer o1 = new User("旭旭宝宝");
Observer o2 = new User("枪魂冰子");
Observer o3 = new User("剑舞红颜笑");
server.login(o1);
server.login(o2);
server.login(o3);
server.setInformation("XXX强化别云剑-无用 +16失败!");
System.out.println("----------------------------------------------");
server.logout(o1);
server.setInformation("XXX强化别云剑-无用 +15失败!");
}
}
可以看到输出为:
DNF公告: XXX强化别云剑-无用 +16失败!
旭旭宝宝面板: XXX强化别云剑-无用 +16失败!
枪魂冰子面板: XXX强化别云剑-无用 +16失败!
剑舞红颜笑面板: XXX强化别云剑-无用 +16失败!
----------------------------------------------
DNF公告: XXX强化别云剑-无用 +15失败!
枪魂冰子面板: XXX强化别云剑-无用 +15失败!
剑舞红颜笑面板: XXX强化别云剑-无用 +15失败!
因为第二次test时我们将观察者o1登出了,所以可以看到第二次消息订阅中已经没有o1对象了。
spring实现:
在spring中封装了关于观察者模式的相关方法,可通过ApplicationEventPublisher注入来进行消息发布
- 消息发布:
@Component
public class Publisher implements CommandLineRunner {
private final ApplicationEventPublisher applicationEventPublisher;
@Autowired
public Publisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
@Override
public void run(String... args) throws Exception {
try {
applicationEventPublisher.publishEvent(new Event("XXX强化别云剑-无用 +16失败!"));
}
catch (Exception ignore) {
}
}
}
- 消息类型实体(需要继承ApplicationEvent )
public class Event extends ApplicationEvent {
public Event(Object source) {
super(source);
}
}
- 创建消费者
@Component
public class Listener implements ApplicationListener<Event> {
@Override
@Async
public void onApplicationEvent(Event event) {
String s = event.getSource().toString();
System.out.println("旭旭宝宝面板:" + s);
}
}
@Component
public class Listenercopy implements ApplicationListener<Event> {
@Override
@Async
public void onApplicationEvent(Event event) {
String s = event.getSource().toString();
System.out.println("枪魂冰子面板:" + s);
}
}
@Component
public class Listenercopy2 implements ApplicationListener<Event> {
@Override
@Async
public void onApplicationEvent(Event event) {
String s = event.getSource().toString();
System.out.println("剑舞红颜笑面板: " + s);
}
}
运行spring程序,可以看到输出为:
旭旭宝宝面板:XXX强化别云剑-无用 +16失败!
枪魂冰子面板:XXX强化别云剑-无用 +16失败!
剑舞红颜笑面板: XXX强化别云剑-无用 +16失败!
时在中春,阳和方起