[design pattern](2) Observer
前言
在上一个博客中我们介绍了Strategy模式,它是行为型模式麾下的一员大将。那么本博客我们来学习一下行为型模式麾下的另一员大将Observer模式。
思考题
老套路,先来思考下面的问题:
问题:思考报纸订阅的问题,有一个体育报社每天都会有新的体育报道,关注体育新闻的用户会获取每天的报道
首先,我们还是来看一看不使用设计模式的代码:
SportNews.java:
public class SportNews {
private String context;
//用户获取新闻
public String getContext() {
return context;
}
//产生新的新闻
public void newContext(String context) {
this.context = context;
}
}
People.java:
public class People {
private final String name;
private SportNews sportNews;
public People(String name, SportNews sportNews) {
this.sportNews = sportNews;
this.name = name;
}
//读新闻
public void readNews() {
System.out.println(String.format("I am %s,I am looking %s", name, sportNews.getContext()));
}
}
Robot.java:
public class Robot {
private static int num = 0;
private final int id = num++;
private SportNews sportNews;
public Robot(SportNews sportNews) {
this.sportNews = sportNews;
}
public void readNews() {
System.out.println(String.format("Robot:%s,Context:%s", id, sportNews.getContext()));
}
}
测试程序:
TestMain.java:
public class TestMain {
public static void main(String... ars) {
SportNews sportNews = new SportNews();
People xiaoLi = new People("xiao li", sportNews);
People xiaoMin = new People("xiao min", sportNews);
Robot robot = new Robot(sportNews);
sportNews.newContext("H:F 1:1");
xiaoLi.readNews();
xiaoMin.readNews();
robot.readNews();
sportNews.newContext("湖人:火箭 102:103");
xiaoLi.readNews();
xiaoMin.readNews();
robot.readNews();
}
}
print:
I am xiao li,I am looking H:F 1:1
I am xiao min,I am looking H:F 1:1
Robot:0,Context:H:F 1:1
I am xiao li,I am looking 湖人:火箭 102:103
I am xiao min,I am looking 湖人:火箭 102:103
Robot:0,Context:湖人:火箭 102:103
上面的代码展示了xiaoli、xiaomin、Robot是怎样获取新的新闻内容的,每个人都需要调用 readNews 才可以获取新的新闻内容。如果再增加一个人,那么这个人也需要调用相应的方法才可以获取。这就相当于每个人都知道报社的地址,每天都要去报社自己拿新的报纸。那么怎样才能改变这样的关系,让人不需要去报社拿新的报纸,那就是使用观察这模式,下面就让我们来具体的介绍这个模式。
介绍Observser
- 定义: 定义对象之间一对多的关系,当一个对象的状态发生改变时,该对象所有依赖对象都会得到通知。
- 类图:

从上面的类图中可以总结一下几点:
- 抽象主题类(Observable):持有一个 Observer 的集合List,通过 registerObserver 方法可以将 Observer 注册到集合中。通过 notifyServers 方法可以让所有在集合里面的 Observer 都获得通知
- 抽象观察者类(Observer):持有一个 Observable ,其目的是为了在 Observable 的集合中取消订阅。而注册行为是发生在构造函数中
具体主题类(ConcreteObservable):继承了 Observable ,并且实现了父类的抽象方法 changeState ,在该方法中调用了父类的方法 notifyObservers
- 具体观察者类(ConcreteObserver):继承了 Observer ,并且实现了父类的方法 update 方法,该方法是具体观察者要做的动作
重构思考题
通过上面的学习,我们已经基本上了解了观察者模式了,下面我们针对上面提到的问题,使用观察者模式来实现它:
首先,我们定义一个抽象主题类:
Subject.java:
import java.util.List;
import java.util.ArrayList; public abstract class Subject {
protected List<Observer> observers; public Subject() {
observers = new ArrayList<Observer>();
} public boolean registerObserver(Observer observer) {
return observers.add(observer);
} public boolean removeObserver(Observer observer) {
return observers.remove(observer);
} public void notifyObservers(String context) {
for(Observer observer: observers) {
observer.update(context);
}
} public abstract void newContext(String context);
}
然后,我们来实现抽象观察者:
Observer.java:
public abstract class Observer {
private Subject subject;
public Observer(Subject subject) {
this.subject = subject;
subject.registerObserver(this);
}
public abstract void update(String context);
}
之后,实现一个具体主题类:
SportNews.java:
public class SportNews extends Subject {
@Override
public void newContext(String context) {
notifyObservers(String.format("sport news:%s", context));
}
}
最后,实现具体观察者类:
People.java:
public class People extends Observer {
private final String name;
public People(String name, Subject subject) {
super(subject);
this.name = name;
}
@Override
public void update(String context) {
System.out.println(String.format("I am %s,I am looking %s", name, context));
}
}
Robot.java:
public class Robot extends Observer {
private static int num = 0;
private final int id = num++;
public Robot(Subject subject) {
super(subject);
}
@Override
public void update(String context) {
System.out.println(String.format("Robot:%s,Context:%s", id, context));
}
}
测试用例:
TestMain.java:
public class TestMain {
public static void main(String... ars) {
Subject subject = new SportNews();
People xiaoLi = new People("xiao li", subject);
People xiaoMin = new People("xiao min", subject);
Robot robot = new Robot(subject);
subject.newContext("H:F 1:1");
subject.newContext("湖人:火箭 102:103");
}
}
以上就是观察者模式的全部代码,通过使用观察者模式,可以看出我们只需要将观察者注册到主题中。不需要调用观察者的任何方法,就可以自动的获得通知。极大的减少了代码量和维护成本。这就相当于你只要订阅了报纸,每天都会有人给你送过去,而不需要自己去报社拿。
扩展
在我们的java中也定义了观察者模式的接口,并且Java中的事件监听机制就是使用观察者模式实现的,它们分别是 java.util.Observer 和 java.util.Observable 。通过实现这两个接口我们可以实现自己的观察者模式,下面就让我们来看看这两个类。
类图:

简单介绍下上面的类图:
- 抽象主题类(Observable): addObserver 是用来注册观察者的。 notifyObservers 是用来通知所有的观察者的。 setChanged 是用来告知 notifyObservers 状态已经改变,可以通知了。其他的就不做介绍了。
下面让我们使用java提供的观察者模式接口来实现上面的问题:
People.java:
import java.util.Observer;
import java.util.Observable;
public class People implements Observer {
private final String name;
private Observable observable;
public People(String name, Observable o) {
this.name = name;
observable = o;
observable.addObserver(this);
} @Override
public void update(Observable o, Object arg) {
System.out.println(String.format("I am %s,I am looking %s", name, arg.toString()));
}
}
Robot.java:
import java.util.Observer;
import java.util.Observable; public class Robot implements Observer {
private static int num = 0;
private final int id = num++;
private Observable observable; public Robot(Observable observable) {
this.observable = observable;
observable.addObserver(this);
} @Override
public void update(Observable o, Object arg) {
System.out.println(String.format("Robot:%s,Context:%s", id, arg.toString()));
}
}
SportNews.java:
import java.util.Observable;
public class SportNews extends Observable {
public void newContext(String context) {
setChanged();
notifyObservers(context);
}
}
测试用例:
TestMain.java:
public class TestMain {
public static void main(String... ars) {
SportNews sportNews = new SportNews();
People xiaoLi = new People("xiao li", sportNews);
People xiaoMin = new People("xiao min", sportNews);
Robot robot = new Robot(sportNews);
sportNews.newContext("H:F 1:1");
sportNews.newContext("湖人:火箭 102:103");
}
}
以上是本博客的全部内容,希望看完会对你有一定的启发。
[design pattern](2) Observer的更多相关文章
- Design Pattern: Observer Pattern
1. Brief 一直对Observer Pattern和Pub/Sub Pattern有所混淆,下面打算通过这两篇Blog来梳理这两种模式.若有纰漏请大家指正. 2. Use Case 首先我们来面 ...
- Learning JavaScript Design Patterns The Observer Pattern
The Observer Pattern The Observer is a design pattern where an object (known as a subject) maintains ...
- 说说设计模式~大话目录(Design Pattern)
回到占占推荐博客索引 设计模式(Design pattern)与其它知识不同,它没有华丽的外表,没有吸引人的工具去实现,它是一种心法,一种内功,如果你希望在软件开发领域有一种新的突破,一个质的飞越,那 ...
- [转]Design Pattern Interview Questions - Part 4
Bridge Pattern, Composite Pattern, Decorator Pattern, Facade Pattern, COR Pattern, Proxy Pattern, te ...
- [转]Design Pattern Interview Questions - Part 2
Interpeter , Iterator , Mediator , Memento and Observer design patterns. (I) what is Interpreter pat ...
- [转]Design Pattern Interview Questions - Part 1
Factory, Abstract factory, prototype pattern (B) What are design patterns? (A) Can you explain facto ...
- C++ Design Pattern: What is a Design Pattern?
Q: What is a Design Pattern? A: Design Patterns represent solutions to problems what arise when deve ...
- Design Pattern in Simple Examples
Instead of defining what is design pattern lets define what we mean by design and what we mean by pa ...
- java设计模式大全 Design pattern samples in Java(最经典最全的资料)
java设计模式大全 Design pattern samples in Java(最经典最全的资料) 2015年06月19日 13:10:58 阅读数:11100 Design pattern sa ...
随机推荐
- Java基础语法-运算符
1算术运算符 1.1运算符和表达式 运算符:对常量和变量进行操作的符号. 表达式:用运算符把常量或者变量连接起来符合java语法的式子就可以称为表达式. 不同运算符链接的表达式体现的是不同类型的表达式 ...
- Oracle 高版本往低版本备份恢复的方法
1. 高版本的数据库备份恢复到低版本的数据库 有时候回报错, 提示version版本不一致, 2. 其实方法是expdp 导出的时候 增加一个参数就可以了 参数 一般的类型是 version=11.2 ...
- chrome浏览器截长图的方法
1.首先打开一个你想要截图的长页面 2.然后按下F12 3.按Ctrl+Shift+P打开console菜单 4.在有个红对勾的位置输入Capture full size screenshot,找到C ...
- iconv编码转换报错问题
今天,再由ISO-8859编码格式转化为UTF-8格式过程中,出现报错:iconv: 未知 10304 处的非法输入序列. 问题分析:ISO-8859是英文格式的编码方式,不支持中文,为了解决中文支持 ...
- PHP 经典有趣的算法
原文:https://blog.csdn.net/a519395243/article/details/77942913 1.一群猴子排成一圈,按1,2,…,n依次编号.然后从第1只开始数,数到第m只 ...
- vue-split-table【表格合并和编辑插件】
前言 vue-split-table应用的效果图 vue-split-table开源地址,欢迎star,现在已经开源和同步到npm上轻松搞定表格拆分或者合并,编辑,再也不怕被产品怼啦 1.核心源码分析 ...
- P3588 [POI2015]PUS(拓扑排序+线段树)
P3588 [POI2015]PUS 对于每个$(l,r,k)$,将$k$个位置向剩下$r-l-k+1$个位置连边,边权为$1$,这样就保证$k$个位置比剩下的大 先给所有位置填$1e9$保证最优 然 ...
- Linux部署项目 shell脚本启动 及 Centos7开放指定端口
我们首先要在linux上安装好jdk tomcat mysql 这些基本环境,这些可以在楼主的 Linux入门 里面找到. linux部署spring项目 1. 右击项目,maven ...
- 【学习总结】快速上手Linux玩转典型应用-第1章-课程介绍
课程目录链接 快速上手Linux玩转典型应用-目录 1. Linux有什么用 2. 课程安排 3. 课程收获 基本运维能力,等等 END
- idea解除版本控制
解除版本控制删除两个文件: 1.idea中删除vcs.xml 2.在项目文件夹中删除.git 参考:https://blog.csdn.net/qq_37999340/article/details/ ...