简介
如果有这样一个场景,比如:我们在游戏的时候,当我们完成一个主线任务的时候,可以能会同时开启多个支线任务,因此我们的支线任务一定是要等主角完成主线任务后,根据主角完成的质量或者说结果来开启对应的支线任务,因此支线任务就像一个观察者,一直盯着主角主线任务完成到哪里了,什么时候它才能公布出来,这就是观察者模式的实际场景应用。
观察者(Observer)模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。它是对象行为型模式。
这是观察者模式的UML图:
从图中我们可以罗列出它的结构:
- 抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
- 具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
- 抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
- 具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
优缺点
观察者模式是一种对象行为型模式,其主要优点如下:
- 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。
- 目标与观察者之间建立了一套触发机制。
它的主要缺点如下:
- 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
- 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
代码实现
抽象主题角色:
package com.example.demo.basis.design.observer;
import java.util.ArrayList;
/**
* @author: sunzhinan
* @create: 2020-08-11 22:29
* @description: 抽象角色类
*/
public abstract class Role {
//定义一个集合,里面是存放这个角色有关的支线任务
public ArrayList<SubTask> subTasksList = new ArrayList<>();
//添加支线任务的方法
public void putTask(SubTask subTask){
subTasksList.add(subTask);
}
//子类实现的方法
public abstract void notifyTask();
}
具体主题角色:
package com.example.demo.basis.design.observer;
/**
* @author: sunzhinan
* @create: 2020-08-11 22:39
* @description: 主角1
*/
public class ProtagonistOne extends Role{
@Override
public void notifyTask() {
System.out.println("-----开启支线任务-----");
for ( SubTask subTask : subTasksList) {
subTask.callMe();
}
}
}
抽象观察者角色:
package com.example.demo.basis.design.observer;
/**
* 支线任务接口
*/
public interface SubTask {
public void callMe();
}
具体观察者角色:
package com.example.demo.basis.design.observer;
/**
* @author: sunzhinan
* @create: 2020-08-11 22:31
* @description: 支线任务1
*/
public class SubTaskOne implements SubTask{
@Override
public void callMe() {
System.out.println("支线任务1已开启");
}
}
package com.example.demo.basis.design.observer;
/**
* @author: sunzhinan
* @create: 2020-08-11 22:33
* @description: 支线任务2
*/
public class SubTaskTwo implements SubTask{
@Override
public void callMe() {
System.out.println("支线任务2已开启");
}
}
测试类:
package com.example.demo.basis.design.observer;
/**
* @author: sunzhinan
* @create: 2020-08-11 22:43
* @description: 测试类
*/
public class Test {
public static void main(String[] args) {
Role role = new ProtagonistOne();
role.putTask(new SubTaskOne());
role.putTask(new SubTaskTwo());
role.notifyTask();
}
}
结果:
-----开启支线任务-----
支线任务1已开启
支线任务2已开启
上面的示例就一个简单的观察者模式基本框架,在这里我们可以丰富一下它,比如我们可以新加一个事件,当出现某个事件的时候,我们开启对应的支线任务。
事件:
package com.example.demo.basis.design.observer;
/**
* @author: sunzhinan
* @create: 2020-08-11 22:50
* @description: 事件——通过泛型可以衍生出不同的角色事件类型
*/
public abstract class TaskEvent<T> {
String taskName;
String taskType;
abstract T getSource();
}
package com.example.demo.basis.design.observer;
/**
* @author: sunzhinan
* @create: 2020-08-11 22:51
* @description: Role角色事件类型
*/
public class OneTaskEvent extends TaskEvent<Role>{
Role role;
public OneTaskEvent(String taskName, String taskType, Role role) {
this.taskName = taskName;
this.taskType = taskType;
this.role = role;
}
@Override
Role getSource() {
return this.role;
}
}
其他类也需要做相应的变化
抽象主题角色:
package com.example.demo.basis.design.observer;
import java.util.ArrayList;
/**
* @author: sunzhinan
* @create: 2020-08-11 22:29
* @description: 抽象角色类
*/
public abstract class Role {
String roleType;
public Role(String roleType) {
this.roleType = roleType;
}
//定义一个集合,里面是存放这个角色有关的支线任务
public ArrayList<SubTask> subTasksList = new ArrayList<>();
//添加支线任务的方法
public void putTask(SubTask subTask){
subTasksList.add(subTask);
}
//子类实现的方法
public abstract void notifyTask();
public String getRoleType() {
return roleType;
}
public void setRoleType(String roleType) {
this.roleType = roleType;
}
}
具体主题角色:
package com.example.demo.basis.design.observer;
/**
* @author: sunzhinan
* @create: 2020-08-11 22:39
* @description: 主角1
*/
public class ProtagonistOne extends Role{
public ProtagonistOne(String roleType) {
super(roleType);
}
@Override
public void notifyTask() {
System.out.println("-----开启支线任务-----");
OneTaskEvent oneTaskEvent = new OneTaskEvent("主线任务1", "kill boss", this);
for ( SubTask subTask : subTasksList) {
subTask.callMe(oneTaskEvent);
}
}
}
抽象观察者角色:
package com.example.demo.basis.design.observer;
/**
* 支线任务接口
*/
public interface SubTask {
public void callMe(TaskEvent taskEvent);
}
具体观察者角色:
package com.example.demo.basis.design.observer;
/**
* @author: sunzhinan
* @create: 2020-08-11 22:31
* @description: 支线任务1
*/
public class SubTaskOne implements SubTask{
@Override
public void callMe(TaskEvent taskEvent) {
System.out.println("支线任务1已开启");
System.out.println("主线任务类型是 : " + taskEvent.taskType + " 已完成");
}
}
package com.example.demo.basis.design.observer;
/**
* @author: sunzhinan
* @create: 2020-08-11 22:33
* @description: 支线任务2
*/
public class SubTaskTwo implements SubTask{
@Override
public void callMe(TaskEvent taskEvent) {
System.out.println("支线任务2已开启");
Role source = (Role) taskEvent.getSource();
System.out.println("主角角色是 : " + source.getRoleType());
System.out.println("主线任务是 : " + taskEvent.taskName + " 已完成");
}
}
测试类:
package com.example.demo.basis.design.observer;
/**
* @author: sunzhinan
* @create: 2020-08-11 22:43
* @description: 测试类
*/
public class Test {
public static void main(String[] args) {
Role role = new ProtagonistOne("法师");
role.putTask(new SubTaskOne());
role.putTask(new SubTaskTwo());
role.notifyTask();
}
}
结果:
-----开启支线任务-----
支线任务1已开启
主线任务类型是 : kill boss 已完成
支线任务2已开启
主角角色是 : 法师
主线任务是 : 主线任务1 已完成
所以可以看出,这样的观察者模式内容更加丰富,同时也能感觉代码的可维护性增强了。
其实观察者模式在实际的运用中有很多,我们常说的事件,比如js中的事件,spring中的事件等等都是使用的这个设计模式。
参考文档:《GoF设计模式》