github地址:https://github.com/1711680493
如需了解更多设计模式,请进入我的设计模式专栏
观察者模式
多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为
这种模式有时又称作发布-订阅模式,模型-视图模式,它是对象行为型模式
优缺点
优点:
降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系
目标与观察者之间建立了一套触发机制
缺点:
目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用
当观察者对象很多时,通知的发布会花费很多时间,影响程序效率
观察者模式结构
抽象主题角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加删除观察者对象的方法,
以及通知所有观察者的抽象方法
具体主题角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象
抽象观察者角色:它是一个抽象类或接口,它包含一个更新自己的抽象方法,当接到具体主题的更改通知时被调用
具体观察者角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态.
观察者模式实例
通常,我们会在外边的摊位上买一些东西,这里我就以煎饼果子摊位为例子
抽象主题角色=煎饼摊的环境
具体主题角色=做煎饼的
抽象观察者角色=买煎饼的人心里想的行为
具体观察者角色=实现心里想的行为
/**
* 观察者模式 煎饼摊的例子
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @version 1.0
*/
public class ObserverMode {
public static void main(String[] args) throws InterruptedException {
//创建环境 煎饼摊
PancakeContext context = new Pancake();
//来了两个人卖煎饼 分别是 张三 李四
User zs = new UserImp("张三");
User ls = new UserImp("李四");
context.addUser(zs);
context.addUser(ls);
System.out.println("-------------");
//煎饼摊开始做煎饼了 先来的是张三 所以先做的煎饼是张三的
context.startPancake(zs);
System.out.println("-------------");
//两分钟后 张三煎饼做完了(这里模仿效果 睡眠两秒)
Thread.sleep(2000);
context.submitPancake(zs);
System.out.println("-------------");
//接着马上开始做李四的煎饼
context.startPancake(ls);
System.out.println("-------------");
//一分钟后 王五 也来买煎饼了
Thread.sleep(1000);
User ww = new UserImp("王五");
context.addUser(ww);
System.out.println("-------------");
//李四的煎饼做完了
Thread.sleep(1000);
context.submitPancake(ls);
System.out.println("-------------");
//开始做王五的煎饼
context.startPancake(ww);
System.out.println("-------------");
//王五的煎饼做完了
Thread.sleep(2000);
context.submitPancake(ww);
System.out.println("-------------");
}
}
/**
* 抽象主题角色 对应煎饼摊的环境 拥有添加观察者(用户) 移除观察者(用户)的功能
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @version 1.0
*/
abstract class PancakeContext {
private ArrayList<User> users = new ArrayList<>();
/**
* 提供子类访问此列表
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @return
*/
public ArrayList<User> getUsers() {
return users;
}
/**
* 有新用户卖煎饼了 新增观察者
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @param user
*/
public void addUser(User user) {
users.add(user);
System.out.println(user.name + ":来买煎饼了");
}
/**
* 用户不买或者煎饼完成走人了 删除观察者
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @param user
*/
public void removeUser(User user) {
users.remove(user);
System.out.println(user.name + ":走了");
}
/**
* 开始做煎饼了
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @param user 开始做谁的煎饼了
*/
abstract void startPancake(User user);
/**
* 煎饼完成了
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @param user 谁的煎饼完成了
*/
abstract void submitPancake(User user);
}
/**
* 具体主题角色 煎饼摊(继承煎饼摊的环境) 实现父类的功能
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @version 1.0
*/
class Pancake extends PancakeContext {
@Override
void startPancake(User user) {
//在座的各位买煎饼的都知道煎饼摊开始做煎饼了(提示所有观察者)
//遍历集合 调用观察者的开始煎饼方法
getUsers().forEach(u -> u.pancakeStart(user));
}
@Override
void submitPancake(User user) {
//在座的各位买煎饼的都知道煎饼摊开始做煎饼了(提示所有观察者)
//遍历集合 调用观察者的完成煎饼方法
getUsers().forEach(u -> u.pancakeSubmit(user));
//用户离开煎饼摊 从观察者中删除
removeUser(user);
}
}
/**
* 抽象观察者 定义观察者的一些行为
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @version 1.0
*/
abstract class User {
String name;//用户的名称
/**
* 创建用户的时候必须要有名称
* @param name
*/
User(String name) {
this.name = name;
}
/**
* 做煎饼的开始了
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @param user 做谁的煎饼
*/
abstract void pancakeStart(User user);
/**
* 做煎饼的完成了
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @param user 完成的用户
*/
abstract void pancakeSubmit(User user);
}
/**
* 具体观察者 代表正在买 煎饼的用户
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @version 1.0
*/
class UserImp extends User {
UserImp(String name) {
super(name);
}
@Override
void pancakeStart(User user) {
//煎饼开始,判断是否是自己的
if (this == user) {
System.out.println(name + ":我的煎饼开始做了");
} else {
System.out.println(name + ":做煎饼开始了,快一点吧...");
}
}
@Override
void pancakeSubmit(User user) {
//煎饼完成,判断是否是自己的
if (this == user) {
System.out.println(name + ":我的煎饼完成了,溜了溜了");
} else {
System.out.println(name + ":煎饼做完了,我的煎饼快到了吗?");
}
}
}
扩展
在Java中,通过java.util.Observable类和java.util.Observer接口定义了观察者模式,
只要实现它们的子类就可以编写观察者模式实例
总结
观察者模式就是通过
抽象主题角色(有增删观察者的功能)
具体角色(通知所有观察者),调用事件,遍历集合里的观察者一个个执行
抽象观察者,定义行为,具体观察者,实现行为的反应
需要先将观察者通过抽象主题角色添加进集合,然后通过具体角色触发事件
观察者执行事件