一.觀察者模式
觀察者模式是一種行為型設(shè)計(jì)模式,它定義了一種一對多的依賴關(guān)系,當(dāng)一個(gè)對象的狀態(tài)發(fā)生改變時(shí),其所有依賴者都會收到通知并自動更新。當(dāng)對象間存在一對多關(guān)系時(shí),則使用觀察者模式(Observer Pattern)。比如,當(dāng)一個(gè)對象被修改時(shí),則會自動通知依賴它的對象。觀察者模式屬于行為型模式。
意圖:定義對象間的一種一對多的依賴關(guān)系,當(dāng)一個(gè)對象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對象都得到通知并被自動更新。
主要解決:一個(gè)對象狀態(tài)改變給其他對象通知的問題,而且要考慮到易用和低耦合,保證高度的協(xié)作。
何時(shí)使用:一個(gè)對象(目標(biāo)對象)的狀態(tài)發(fā)生改變,所有的依賴對象(觀察者對象)都將得到通知,進(jìn)行廣播通知。
如何解決:使用面向?qū)ο蠹夹g(shù),可以將這種依賴關(guān)系弱化。
關(guān)鍵代碼:在抽象類里有一個(gè) ArrayList 存放觀察者們。
應(yīng)用實(shí)例:?1、拍賣的時(shí)候,拍賣師觀察最高標(biāo)價(jià),然后通知給其他競價(jià)者競價(jià)。 2、西游記里面悟空請求菩薩降服紅孩兒,菩薩灑了一地水招來一個(gè)老烏龜,這個(gè)烏龜就是觀察者,他觀察菩薩灑水這個(gè)動作。
優(yōu)點(diǎn):?1、觀察者和被觀察者是抽象耦合的。 2、建立一套觸發(fā)機(jī)制。
缺點(diǎn):?1、如果一個(gè)被觀察者對象有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會花費(fèi)很多時(shí)間。 2、如果在觀察者和觀察目標(biāo)之間有循環(huán)依賴的話,觀察目標(biāo)會觸發(fā)它們之間進(jìn)行循環(huán)調(diào)用,可能導(dǎo)致系統(tǒng)崩潰。 3、觀察者模式?jīng)]有相應(yīng)的機(jī)制讓觀察者知道所觀察的目標(biāo)對象是怎么發(fā)生變化的,而僅僅只是知道觀察目標(biāo)發(fā)生了變化。
使用場景:
- 一個(gè)抽象模型有兩個(gè)方面,其中一個(gè)方面依賴于另一個(gè)方面。將這些方面封裝在獨(dú)立的對象中使它們可以各自獨(dú)立地改變和復(fù)用。
- 一個(gè)對象的改變將導(dǎo)致其他一個(gè)或多個(gè)對象也發(fā)生改變,而不知道具體有多少對象將發(fā)生改變,可以降低對象之間的耦合度。
- 一個(gè)對象必須通知其他對象,而并不知道這些對象是誰。
- 需要在系統(tǒng)中創(chuàng)建一個(gè)觸發(fā)鏈,A對象的行為將影響B(tài)對象,B對象的行為將影響C對象……,可以使用觀察者模式創(chuàng)建一種鏈?zhǔn)接|發(fā)機(jī)制。
注意事項(xiàng):?1、JAVA 中已經(jīng)有了對觀察者模式的支持類。 2、避免循環(huán)引用。 3、如果順序執(zhí)行,某一觀察者錯(cuò)誤會導(dǎo)致系統(tǒng)卡殼,一般采用異步方式。
觀察者模式包含以下幾個(gè)核心角色:
- 主題(Subject):也稱為被觀察者或可觀察者,它是具有狀態(tài)的對象,并維護(hù)著一個(gè)觀察者列表。主題提供了添加、刪除和通知觀察者的方法。
- 觀察者(Observer):觀察者是接收主題通知的對象。觀察者需要實(shí)現(xiàn)一個(gè)更新方法,當(dāng)收到主題的通知時(shí),調(diào)用該方法進(jìn)行更新操作。
- 具體主題(Concrete Subject):具體主題是主題的具體實(shí)現(xiàn)類。它維護(hù)著觀察者列表,并在狀態(tài)發(fā)生改變時(shí)通知觀察者。
- 具體觀察者(Concrete Observer):具體觀察者是觀察者的具體實(shí)現(xiàn)類。它實(shí)現(xiàn)了更新方法,定義了在收到主題通知時(shí)需要執(zhí)行的具體操作。
觀察者模式通過將主題和觀察者解耦,實(shí)現(xiàn)了對象之間的松耦合。當(dāng)主題的狀態(tài)發(fā)生改變時(shí),所有依賴于它的觀察者都會收到通知并進(jìn)行相應(yīng)的更新。
二.demo
1.事件監(jiān)聽接口
public interface EventListener {
void doEvent(LotteryResult result);
}
2.實(shí)現(xiàn)監(jiān)聽接口(短信、MQ)
public class MessageEventListener implements EventListener {
private Logger logger = LoggerFactory.getLogger(MessageEventListener.class);
@Override
public void doEvent(LotteryResult result) {
logger.info("給?戶 {} 發(fā)送短信通知(短信):{}", result.getuId(),
result.getMsg());
}
}
public class MQEventListener implements EventListener {
private Logger logger = LoggerFactory.getLogger(MQEventListener.class);
@Override
public void doEvent(LotteryResult result) {
logger.info("記錄?戶 {} 搖號結(jié)果(MQ):{}", result.getuId(),
result.getMsg());
}
}
3.事件管理類
public class EventManager {
Map<Enum<EventType>, List<EventListener>> listeners = new HashMap<>();
public EventManager(Enum<EventType>... operations) {
for (Enum<EventType> operation : operations) {
this.listeners.put(operation, new ArrayList<>());
}
}
public enum EventType {
MQ, Message
}
/**
* 訂閱
*
* @param eventType 事件類型
* @param listener 監(jiān)聽
*/
public void subscribe(Enum<EventType> eventType, EventListener
listener) {
List<EventListener> users = listeners.get(eventType);
users.add(listener);
}
/**
* 取消訂閱
*
* @param eventType 事件類型
* @param listener 監(jiān)聽
*/
public void unsubscribe(Enum<EventType> eventType, EventListener
listener) {
List<EventListener> users = listeners.get(eventType);
users.remove(listener);
}
/**
* 通知
*
* @param eventType 事件類型
* @param result 結(jié)果
*/
public void notify(Enum<EventType> eventType, LotteryResult result) {
List<EventListener> users = listeners.get(eventType);
for (EventListener listener : users) {
listener.doEvent(result);
}
}
}
整個(gè)處理的實(shí)現(xiàn)上提供了三個(gè)主要?法;訂閱( subscribe )、取消訂閱( unsubscribe )、通知 ( notify )。這三個(gè)?法分別?于對監(jiān)聽時(shí)間的添加和使?。
另外因?yàn)槭录胁煌念愋?,這?使?了枚舉的?式進(jìn)?處理,也?便讓外部在規(guī)定下使?事件, ?不?于亂傳信息( EventType.MQ 、 EventType.Message )。
4.業(yè)務(wù)抽象類接?
public abstract class LotteryService {
private EventManager eventManager;
public LotteryService() {
eventManager = new EventManager(EventManager.EventType.MQ,
EventManager.EventType.Message);
eventManager.subscribe(EventManager.EventType.MQ, new
MQEventListener());
eventManager.subscribe(EventManager.EventType.Message, new
MessageEventListener());
}
public LotteryResult draw(String uId) {
LotteryResult lotteryResult = doDraw(uId);
// 需要什么通知就給調(diào)?什么?法
eventManager.notify(EventManager.EventType.MQ, lotteryResult);
eventManager.notify(EventManager.EventType.Message,
lotteryResult);
return lotteryResult;
}
protected abstract LotteryResult doDraw(String uId);
}
5.業(yè)務(wù)接?實(shí)現(xiàn)類
public class LotteryServiceImpl extends LotteryService {
private MinibusTargetService minibusTargetService = new
MinibusTargetService();
@Override
protected LotteryResult doDraw(String uId) {
// 搖號
String lottery = minibusTargetService.lottery(uId);
// 結(jié)果
return new LotteryResult(uId, lottery, new Date());
}
}
6.測試
@Test
public void test(){
LotteryService lotteryService=new LotteryServiceImpl();
LotteryResult result=lotteryService.draw("2765789109876");
logger.info("測試結(jié)果:{}",JSON.toJSONString(result));
}
參考:觀察者模式 | 菜鳥教程文章來源:http://www.zghlxwxcb.cn/news/detail-608745.html
《重學(xué)Java設(shè)計(jì)模式》?文章來源地址http://www.zghlxwxcb.cn/news/detail-608745.html
到了這里,關(guān)于設(shè)計(jì)模式-觀察者模式的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!