[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 ...
随机推荐
- 根据select的option文本选中对应的选项
function selectByOptionTxt(obj,txt){ var optionArr = $(obj).find("option"); for(var i=0;i& ...
- [Python3] 004 字符串的基本使用
目录 1. 字符串简介 1.1 作用 1.2 注意点 2. 使用方式 2.1 用引号括起来 少废话,上例子 2.2 单.双引号可以"轮换交替" 少废话,上例子 3. 转义字符 3. ...
- 部署CM集群首次运行报错:Formatting the name directories of the current NameNode.
1. 报错提示 Formatting the name directories of the current NameNode. If the name directories are not emp ...
- Java文件手动编译执行步骤
Java编译执行步骤: 1)将 Java 代码编写到扩展名为 .java 的文件中.2)通过 javac 命令对该 java 文件进行编译.3)通过 java 命令对生成的 class 文件进行运行. ...
- dp基础√
1.重叠子问题 2.能从小问题推到大问题 Dp: 设计状态+状态转移 状态: 1.状态表示: 是对当前子问题的解的局面集合的一种(充分的)描述.(尽量简洁qwq) ◦ 对于状态的表示 ...
- http协议中常见的状态码以及请求方式,http协议的组成
请求状态码: 2xxx:表示请求成功,例如200. 3xxx:表示请求被重定向,表示完成请求,需要进一步操作,例如 302. 4xxx:表示请求错误,例如:404,资源没有找到. 5xxx:表示服务器 ...
- k3 cloud提示超出产品激活有效期
k3 cloud提示超出产品激活有效期,请联系系统管理员登录管理中心进行产品激活(激活路径:许可中心-许可管理-产品激活) 首先进入管理中心:一次点击许可中心-产品激活 复制激活串号并点击金蝶正版验证 ...
- c++ Socket客户端和服务端示例版本一
客户端 #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <sys/soc ...
- Spring基础15——通过工厂方法来配置bean
1.什么是工厂方法 这里的工厂方法指的是创建指定bean的方法.工厂方法又分为静态工厂方法和实例工厂方法. 2.静态工厂方法配置bean 调用静态工厂方法创建Bean是将对象创建的过程封装到静态方法中 ...
- keepalived容灾方案,实现nginx负载均衡主从架构(1)
一:环境准备:4台nginx服务器,两台用yum安装,两台使用源码安装 第一步:使用yum安装nginx服务器,在浏览器输入ip,可以显示以下内容,这步比较简单,安装好修改/usr/share/ngi ...