观察者模式,适用于一对多的场合,即,当某一处发生了变化,需要通知多个地方去修改,在项目中是非常常用和常见的方法。它还被称为订阅发布模型,需要关注某个变化的,注册到相应的接口,当该变化发生时,即将变化发送通知到注册了的各个用户。
还是举个例子,有一个报社,每天早上都会给定了报纸的人家准时发送报纸。我们专业一点来分析一下:
1.报社可以接收新的家庭订报纸
2.报社将退订和到期的家庭去掉
3.每天报社要准时送报纸,这就是一个有消息的触发的机制
4.订报纸的家庭每天收到报纸
我们来看代码实现
/**
* @author: hx
* @Time: 2019/5/6
* @Description: Family,所有订报纸的家庭都有的方法
*/
public interface Family {
/**
* 收到报纸
*/
void receive(String newspaper);
}
/**
* @author: hx
* @Time: 2019/5/6
* @Description: FamilyChen 老陈家
*/
public class FamilyChen implements Family {
@Override
public void receive(String newspaper) {
System.out.println("Chen-收到今日的报纸:"+newspaper);
}
}
/**
* @author: hx
* @Time: 2019/5/6
* @Description: FamilyHu 老胡家
*/
public class FamilyHu implements Family {
@Override
public void receive(String newspaper) {
System.out.println("Hu-收到今日的报纸:"+newspaper);
}
}
/**
* @author: hx
* @Time: 2019/5/6
* @Description: NewsPaperOffice 报社的基本方法
*/
public interface NewsPaperOffice {
/**
* 添加订报纸的家庭
* @param family
*/
void add(Family family);
/**
* 去掉到期或者退订的家庭
* @param family
*/
void del(Family family);
/**
* 送报纸
*/
void send(String newspaper);
}
/**
* @author: hx
* @Time: 2019/5/6
* @Description: MorningNewspaperOffice,一家早上送报纸的具体的报社
*/
public class MorningNewspaperOffice implements NewsPaperOffice {
private List mFamilies = new ArrayList<>();
@Override
public void add(Family family) {
mFamilies.add(family);
}
@Override
public void del(Family family) {
mFamilies.remove(family);
}
@Override
public void send(String newspaper) {
for (Family family : mFamilies){
family.receive(newspaper);
}
}
}
public static void main(String[] args){
NewsPaperOffice newsPaperOffice = new MorningNewspaperOffice();
Family familyHu = new FamilyHu();
Family familyChen = new FamilyChen();
newsPaperOffice.add(familyHu);
newsPaperOffice.add(familyChen);
newsPaperOffice.send("今日头条:共产党万岁");
newsPaperOffice.del(familyHu);
newsPaperOffice.send("今日头条:新中国你好");
}
看一下运行日志
Hu-收到今日的报纸:今日头条:共产党万岁
Chen-收到今日的报纸:今日头条:共产党万岁
Chen-收到今日的报纸:今日头条:新中国你好
使用观察者模式,可以非常友好的将代码解耦,并且使业务流程变得十分清晰。
但是在实际的项目中,这样的观察者模式仅仅只完成了一半,还有另外一半是消息的产生和管理,然后依次对消息进行处理。
假设这家报社,是一家实时新闻报社,会不停的去获取到最新的消息并送到订阅了的家庭,那对消息就需要进行管理并处理。
我们写一个线程模拟一下,每5秒就产生一条新的消息,报社收到消息,放入到处理线程,然后再发送到各个家庭
新增一个消息产生的类News
/**
* @author: hx
* @Time: 2019/5/6
* @Description: News
*/
public class News{
private NewsListener mNewsListener;
/**
* 定时任务线程池
*/
private ScheduledThreadPoolExecutor mScheduledThreadPoolExecutor;
/**
* 创建线程工厂
*/
private ThreadFactory threadFactory;
/**
* 定时产生新闻的任务
*/
private ScheduledFuture> newsScheduledFuture;
private int i = 0;
/**
* 产生消息的线程
*/
private Runnable makeNews = new Runnable() {
@Override
public void run() {
String news = "第"+i+"条新闻";
mNewsListener.sendNews(news);
i++;
}
};
public News(NewsListener newsListener) {
mNewsListener = newsListener;
threadFactory = new ThreadFactoryBuilder().setNameFormat("News_pool").build();
mScheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(3, threadFactory);
newsScheduledFuture = mScheduledThreadPoolExecutor.scheduleAtFixedRate(makeNews,0,5, TimeUnit.SECONDS);
}
}
新增一个将消息发送出来的接口
/**
* @author: hx
* @Time: 2019/5/6
* @Description: NewsListener
*/
public interface NewsListener {
/**
* 收到消息后处理的方法
* @param news
*/
void sendNews(String news);
}
新增一个消息管理的类继承Thread,实现消息监听接口,用于接收News发送出来的消息
/**
* @author: hx
* @Time: 2019/5/6
* @Description: NewsManager
*/
public class NewsManager extends Thread implements NewsListener {
/**
* //存储消息的队列
*/
private LinkedList storage;
private NewsPaperOffice mNewsPaperOffice;
public NewsManager(NewsPaperOffice newsPaperOffice) {
mNewsPaperOffice = newsPaperOffice;
storage = new LinkedList<>();
this.start();
}
@Override
public void run() {
while (true) {
consumeNews();
}
}
/**
* 消费队列中的消息
*/
private void consumeNews() {
if (storage.isEmpty()) {
sleepConsume();
} else {
String news = storage.getFirst();
storage.poll();
mNewsPaperOffice.send(news);
}
}
/**
* 队列内无处理事件,停止循环
*/
private void sleepConsume() {
synchronized (this) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 向队列里放入消息
*
* @param news
*/
private void produceMsg(String news) {
synchronized (storage) {
storage.offer(news);
}
synchronized (this) {
this.notify();
}
}
@Override
public void sendNews(String news) {
produceMsg(news);
}
}
修改一下调用的入口
public static void main(String[] args){
NewsPaperOffice newsPaperOffice = new MorningNewspaperOffice();
Family familyHu = new FamilyHu();
Family familyChen = new FamilyChen();
newsPaperOffice.add(familyHu);
newsPaperOffice.add(familyChen);
NewsManager newsManager = new NewsManager(newsPaperOffice);
new News(newsManager);
}
输出结果如下
Hu-收到今日的报纸:第0条新闻
Chen-收到今日的报纸:第0条新闻
Hu-收到今日的报纸:第1条新闻
Chen-收到今日的报纸:第1条新闻
Hu-收到今日的报纸:第2条新闻
Chen-收到今日的报纸:第2条新闻
Hu-收到今日的报纸:第3条新闻
Chen-收到今日的报纸:第3条新闻
Hu-收到今日的报纸:第4条新闻
Chen-收到今日的报纸:第4条新闻
在项目中基本都是这样来运用观察者模式,从消息的产生,消息的消费到消息的分发由NewsManager来管理,订阅和发布则由观察者模式组成的代码来管理,分别控制消息和订阅者,形成一个闭环系统。