概念
首先說明一下,發佈-訂閱模式並不等同於觀察者模式,這兩者是有區別的。舉例說明,用戶直接向出版社訂閱雜誌,出版社直接把雜誌發送給訂閱雜誌的用戶,這種場景就是觀察者模式。而發佈-訂閱模式則不同,出版社和用戶並不直接接觸,用戶是向郵局訂閱雜誌,出版社向郵局發佈雜誌後,郵局再向用戶派送雜誌。也就是說,發佈-訂閱模式是有一箇中轉調度中心的。如下圖:
上圖發佈訂閱模式進行抽象如下圖,借圖一用:
發佈訂閱模式角色
- 發佈者:消息或者說主題的生產者。
- 訂閱者:訂閱消息或主題的消費者。
- 調度中心/消息管理器:生產者發佈消息與消費者訂閱消息的中轉站,調度作用。
- 主題:也稱消息,生產者生產與消費者使用的內容。
代碼實現
案例場景:拓展《設計模式入門–觀察者模式》案例二場景(資訊網站每天給註冊用戶推送新聞)爲:很多自媒體作者向資訊網站投內容,文章被採用後,適時給註冊用戶推送文章。作者是消息發佈者,註冊用戶是訂閱者,調度中心是網站平臺,主題則爲內容(文章、廣告、活動)。
思路:其實就是實現上述四個角色,這裏以最簡單的方式實現。
發佈者:能夠向平臺提交內容
訂閱者:能夠從平臺獲取推送的內容、然後讀內容
內容:文章、廣告、活動,就是一個封裝的實體類
調度中心:能夠存儲發佈者提交的內容,能夠添加、刪除訂閱者,能夠推送消息給訂閱者
發佈者->
- 抽象發佈者基類
package learn.publishsubscribe.example;
/**
* 抽象發佈者
* Created by chao.du on 2018/2/1.
*/
public interface BaseAuthor<T> {
/**
* 發佈消息(作者向網站提交文章)
* @param contentHelper 消息管理器,訂閱器
*/
void pushlishContent(ContentHelper contentHelper,T content);
}
- 具體發佈者
package learn.publishsubscribe.example;
/**
* 具體發佈者1,作者1
* Created by chao.du on 2018/2/1.
*/
public class AuthorA<T extends BaseContent> implements BaseAuthor<T> {
private String name;
public void pushlishContent(ContentHelper contentHelper,T content) {
contentHelper.publishContentToQueue(content);
}
public AuthorA(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/**
* 具體發佈者2,作者2
* Created by chao.du on 2018/2/1.
*/
public class AuthorB<T extends BaseContent> implements BaseAuthor<T> {
private String name;
public AuthorB(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void pushlishContent(ContentHelper contentHelper,T content) {
contentHelper.publishContentToQueue(content);
}
}
訂閱者->
- 抽象訂閱者基類
package learn.publishsubscribe.example;
/**
* 抽象訂閱者
* Created by chao.du on 2018/2/1.
*/
public abstract class BaseReader {
/**
* 消息
*/
protected BaseContent content;
/**
* 讀者名字
*/
protected String name;
public BaseReader(String name) {
this.name = name;
}
/**
* 訂閱消息(註冊用戶向網站訂閱文章)
* @param contentHelper 消息管理器,訂閱器
*/
void subscribeContent(ContentHelper contentHelper){
contentHelper.addReader(this);
}
/**
* 收到內容處理器推送的消息(註冊用戶接手某作者的創作內容)
* @param content 文章
*/
void receiveContent(BaseContent content){
this.content=content;
String title=null;
if(content instanceof Article){
title=((Article) content).getTitle();
}else if(content instanceof Advertisement){
title=((Advertisement) content).getTitle();
}else{
title=((Activity) content).getTitle();
}
System.out.println(this.name+":收到來自【"+this.content.getWriteBy()+"】的"+this.content.getContentType()+"【"+title+"】");
this.readContent(this.content);
}
/**
* 收到消息,進行後續邏輯(讀消息)
* @param content
*/
abstract void readContent(BaseContent content);
}
- 具體訂閱者
package learn.publishsubscribe.example;
/**
* 具體訂閱者,註冊者1
* Created by chao.du on 2018/2/1.
*/
public class Reader1 extends BaseReader {
public Reader1(String name) {
super(name);
}
public void readContent(BaseContent content) {
System.out.println(this.name+"----這篇文章我讀了1111");
}
}
package learn.publishsubscribe.example;
/**
* 具體訂閱者,註冊者2
* Created by chao.du on 2018/2/1.
*/
public class Reader2 extends BaseReader {
public Reader2(String name) {
super(name);
}
public void readContent(BaseContent content) {
System.out.println(this.name+"----這篇文章我讀了2222");
}
}
package learn.publishsubscribe.example;
/**
* 具體訂閱者,註冊者3
* Created by chao.du on 2018/2/1.
*/
public class Reader3 extends BaseReader {
public Reader3(String name) {
super(name);
}
public void readContent(BaseContent content) {
System.out.println(this.name+"----這篇文章我讀了3333");
}
}
消息->
- 消息基類
package learn.publishsubscribe.example;
import java.util.Date;
/**
* 消息類,主題,內容類型:文章、廣告、活動等
* Created by chao.du on 2018/2/1.
*/
public abstract class BaseContent {
protected String contentType;
protected Date createTime;
protected String writeBy;
public BaseContent() {
this.createTime=new Date();
}
public BaseContent(String contentType) {
this.contentType=contentType;
this.createTime=new Date();
}
public BaseContent(String contentType, String writeBy) {
this.contentType=contentType;
this.writeBy=writeBy;
this.createTime=new Date();
}
public String getContentType() {
return contentType;
}
public Date getCreateTime() {
return createTime;
}
public String getWriteBy() {
return writeBy;
}
public void setWriteBy(String writeBy) {
this.writeBy = writeBy;
}
}
- 具體消息類
package learn.publishsubscribe.example;
/**
* 內容--文章類
* Created by chao.du on 2018/2/1.
*/
public class Article extends BaseContent {
/**
* 標題
*/
private String title;
/**
* 標籤
*/
private String tag;
/**
* 簡介
*/
private String descrition;
/**
* 正文
*/
private String content;
public Article() {
super("文章");
}
public Article(String writeBy, String title, String tag, String descrition, String content) {
super("文章",writeBy);
this.title = title;
this.tag = tag;
this.descrition = descrition;
this.content = content;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
public String getDescrition() {
return descrition;
}
public void setDescrition(String descrition) {
this.descrition = descrition;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
package learn.publishsubscribe.example;
/**
* 內容--廣告類
* Created by chao.du on 2018/2/1.
*/
public class Advertisement extends BaseContent {
/**
* 廣告標題
*/
private String title;
/**
* 廣告banner地址
*/
private String banner;
/**
* 廣告描述
*/
private String descrtion;
public Advertisement() {
super("廣告");
}
public Advertisement(String writeBy, String title, String banner, String descrtion) {
super("廣告",writeBy);
this.title = title;
this.banner = banner;
this.descrtion = descrtion;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getBanner() {
return banner;
}
public void setBanner(String banner) {
this.banner = banner;
}
public String getDescrtion() {
return descrtion;
}
public void setDescrtion(String descrtion) {
this.descrtion = descrtion;
}
}
package learn.publishsubscribe.example;
import java.util.Date;
/**
* 內容--活動類
* Created by chao.du on 2018/2/1.
*/
public class Activity extends BaseContent {
/**
* 活動標題
*/
private String title;
/**
* 活動介紹
*/
private String content;
/**
* 活動時間
*/
private Date date;
public Activity() {
super("活動");
}
public Activity(String title, String content, Date date) {
super("活動");
this.title = title;
this.content = content;
this.date = date;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
調度中心->
package learn.publishsubscribe.example;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
/**
* 消息管理器,主題調度中心,網站文章處理器
* Created by chao.du on 2018/2/1.
*/
public class ContentHelper{
/**
* 內容調度器註冊訂閱內容的讀者(網站註冊者)容器
*/
private List<BaseReader> readerList=new ArrayList<BaseReader>();
/**
* 發佈者發佈消息的消息存儲隊列
*/
private Queue<BaseContent> queue = new LinkedList();
public ContentHelper() {
}
public ContentHelper(List<BaseReader> readerList) {
this.readerList = readerList;
}
/**
* 向消息隊列添加消息(供發佈者發佈信息調用使用)
* @param content
*/
public void publishContentToQueue(BaseContent content){
this.queue.offer(content);
for(BaseReader reader:readerList){
reader.receiveContent(content);
}
}
/**
* 向訂閱者推送消息(供訂閱者獲取內容處理器推送的消息,訂閱者調用)
* @return
*/
public void pushContentFromQueue(){
for (BaseReader reader:readerList){
reader.receiveContent(this.queue.poll());
}
}
/**
* 添加訂閱者
*/
public void addReader(BaseReader reader){
this.readerList.add(reader);
}
/**
* 刪除訂閱者
*/
public void removeReader(BaseReader reader){
this.removeReader(reader);
}
}
客戶端->
package learn.publishsubscribe.example;
/**
* 場景:很多作者向資訊平臺提交消息內容(文章、廣告、活動),資訊平臺向平臺註冊用戶推送消息內容
* Created by chao.du on 2018/2/2.
*/
public class Client {
public static void main(String[] args) {
//內容處理器
ContentHelper contentHelper=new ContentHelper();
//訂閱人,訂閱消息
new Reader1("小明").subscribeContent(contentHelper);
new Reader2("小張").subscribeContent(contentHelper);
new Reader3("小花").subscribeContent(contentHelper);
//發佈人,發佈消息
new AuthorA("美食博主").pushlishContent(contentHelper,new Article("美食博主","紅燒肉絕佳菜譜","紅燒肉","很好的紅燒肉菜譜","步驟一、二、三"));
new AuthorB("廣告人").pushlishContent(contentHelper,new Advertisement("廣告人","營銷黃金法則","http://www.baidu.com/1.jpg","學會營銷。。。。。"));
}
}
運行結果->
參考資料:
1、《訂閱消息模式》