在商城系統中使用設計模式----策略模式之在spring中使用觀察者模式和發佈/訂閱

1.概念:java

觀察者模式:git

  是屬於設計者模式中的一種,一個目標對象管理全部相依於它的觀察者對象,而且在它自己的狀態改變時主動發出通知。github

發佈/訂閱:spring

  是一種消息範式,消息的發送者(稱爲發佈者)不會將消息直接發送給特定的接收者(稱爲訂閱者),而是經過調度器將消息發佈給訂閱者。springboot

 

2.區別:下圖明顯能夠看出發佈/訂閱比觀察者模式中多了一層中間信道,app

  

  • Observer模式中,O bservers知道Subject,同時Subject還保留了Observers的記錄然而,在發佈者/訂閱者中,發佈者和訂閱者不須要彼此瞭解他們只是在消息隊列或代理的幫助下進行通訊。
  • Publisher / Subscriber模式中,組件是鬆散耦合的,而不是Observer模式
  • 觀察者模式主要以同步方式實現,即當某些事件發生時,Subject調用其全部觀察者的適當方法。發行者/訂戶圖案在一個實施大多異步方式(使用消息隊列)。
  • 觀察者模式須要在單個應用程序地址空間中實現。另外一方面,發佈者/訂閱者模式更像是跨應用程序模式。

3.使用場景:異步

  當用戶下單成功後,要執行 修改訂單狀態,分傭,通知店主發貨。ide

 

4.實現一:觀察者模式:spring-boot

 (1)  java.util包提供了對該模式的支持,提供了Observer(觀察者)方法和Obervable(被觀察者)方法。post

package java.util;

public interface Observer {
    /**
   * 每當觀察對象發生變化時,都會調用此方法。
     *
     * @param   o     the observable object.
     * @param   arg   an argument passed to the <code>notifyObservers</code>
     *                 method.
     */
    void update(Observable o, Object arg);
}
package java.util;


public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs;

    /** Construct an Observable with zero Observers. */
    public Observable() {
        obs = new Vector<>();
    }

    /**
     *  添加觀察者
     * @param   o   an observer to be added.
     * @throws NullPointerException   if the parameter o is null.
     */
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }

    /**
     * 刪除觀察者
     * @param   o   the observer to be deleted.
     */
    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }

    /**    
   * 方法被調用的時候 * 通知觀察者 * * @see java.util.Observable#clearChanged() * @see java.util.Observable#hasChanged() * @see java.util.Observer#update(java.util.Observable, java.lang.Object) */ public void notifyObservers() { notifyObservers(null); } /** * 通知觀察者*/ public void notifyObservers(Object arg) { /* * a temporary array buffer, used as a snapshot of the state of * current Observers. */ Object[] arrLocal; synchronized (this) { /*
        * 同步保證在通知的時候,此資源不被其餘所佔用,避免發生在這個時候觀察者發生變化
        *
*/ if (!changed) return; arrLocal = obs.toArray(); clearChanged(); } for (int i = arrLocal.length-1; i>=0; i--) ((Observer)arrLocal[i]).update(this, arg); } /** * Clears the observer list so that this object no longer has any observers. */ public synchronized void deleteObservers() { obs.removeAllElements(); } /** * Marks this <tt>Observable</tt> object as having been changed; the * <tt>hasChanged</tt> method will now return <tt>true</tt>. */ protected synchronized void setChanged() { changed = true; } /** * Indicates that this object has no longer changed, or that it has * already notified all of its observers of its most recent change, * so that the <tt>hasChanged</tt> method will now return <tt>false</tt>. * This method is called automatically by the * <code>notifyObservers</code> methods. * * @see java.util.Observable#notifyObservers() * @see java.util.Observable#notifyObservers(java.lang.Object) */ protected synchronized void clearChanged() { changed = false; } /** * Tests if this object has changed. * * @return <code>true</code> if and only if the <code>setChanged</code> * method has been called more recently than the * <code>clearChanged</code> method on this object; * <code>false</code> otherwise. * @see java.util.Observable#clearChanged() * @see java.util.Observable#setChanged() */ public synchronized boolean hasChanged() { return changed; } /** * Returns the number of observers of this <tt>Observable</tt> object. * * @return the number of observers of this object. */ public synchronized int countObservers() { return obs.size(); } }

  (2)被觀察者繼承Observable類。

import java.util.Observable;

/**
 * @description:被觀察者
 * @author: Chen
 * @create: 2019-04-03 23:01
 **/
public class PayObservable extends Observable {



    @Override
    public void notifyObservers() {
        System.out.println("有人下單啦");
        setChanged();
        super.notifyObservers();
    }
}

(3)觀察者繼承Observer接口

/**
 * @description:佣金觀察者
 * @author: Chen
 * @create: 2019-04-03 22:55
 **/
public class CommissionObserver implements Observer {

    public CommissionObserver(Observable observable){
        observable.addObserver(this);
    }

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("你們注意了,有傻逼下單了,我要開始分佣金了");
    }
}
package com.chen.observer;

import java.util.Observable;
import java.util.Observer;

/**
 * @description:消息觀察者
 * @author: Chen
 * @create: 2019-04-03 22:50
 **/
public class MsgObserver implements Observer {

    public MsgObserver(Observable observable){
        observable.addObserver(this);
    }

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("管理員,有人下單啦。");
    }
}
package com.chen.observer;

import java.util.Observable;
import java.util.Observer;

/**
 * @description:訂單觀察者
 * @author: Chen
 * @create: 2019-04-03 22:49
 **/
public class OrderObserver implements Observer {

    public OrderObserver(Observable observable){
        observable.addObserver(this);
    }

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("趕忙把訂單改爲已支付");
    }
}

(4)調用入口

/**
 * @description:當用戶下單成功後,要執行 修改訂單狀態,分傭,通知店主發貨
 * @author: Chen
 * @create: 2019-04-03 22:34
 **/
public class ObserverMain {

    public static void main(String[] args) {

        PayObservable payObservable = new PayObservable();

        new CommissionObserver(payObservable);
        new MsgObserver(payObservable);
        new OrderObserver(payObservable);

        payObservable.notifyObservers();
    }
}

執行結果:

 

源碼地址:商城中的觀察者模式

 

實現二:發佈/訂閱

springboot有提供一些支持發佈/訂閱模式的基本類,咱們如圖只需關心,發佈者發佈事件,訂閱者接收事件。不會去理會事件通道如何廣播通知。

 

(1)springboot提供的基礎類:

package org.springframework.context;

import java.util.EventObject;

/**
 * 全部應用程序事件都要擴展的類。
* 發佈者將此事件發佈出去,訂閱者根據事件類型去判斷,進行業務處理
*/ public abstract class ApplicationEvent extends EventObject { /** use serialVersionUID from Spring 1.2 for interoperability. */ private static final long serialVersionUID = 7099057708183571937L; /** System time when the event happened. */ private final long timestamp; /** * Create a new ApplicationEvent. * @param source the object on which the event initially occurred (never {@code null}) */ public ApplicationEvent(Object source) { super(source); this.timestamp = System.currentTimeMillis(); } /** * Return the system time in milliseconds when the event happened. */ public final long getTimestamp() { return this.timestamp; } }
package org.springframework.context;

import java.util.EventListener;

/**
* 訂閱者繼承此接口 
* 應用程序事件偵聽器實現的接口 * 繼承此接口的類,要注入到spring容器中,交給bean工廠進行管理,這樣在廣播到時候,才能接收到信息*/ @FunctionalInterface public interface ApplicationListener<E extends ApplicationEvent> extends EventListener { /** * 接收到事件後進行處理 */ void onApplicationEvent(E event); }
package org.springframework.context;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.Aware;


/**
* spring容器包裝接口
* 發佈者繼承此接口,調用容器中publishEvent(ApplicationEvent event)方法,發佈事件。
*/ public interface ApplicationContextAware extends Aware { /** * 設置spring容器到此處*/ void setApplicationContext(ApplicationContext applicationContext) throws BeansException; }

 

(2)支付事件繼承ApplicationEvent類。

import org.springframework.context.ApplicationEvent;

/**
 * @description:支付事件,有人支付了訂單
 * @author: Chen
 * @create: 2019-04-21 10:26
 **/
public class PayEvent extends ApplicationEvent {

    public PayEvent(Object source) {
        super(source);
        System.out.println("MsgEvent:"+source.toString());
    }
}

(3)訂閱者繼承ApplicationListener接口

/**
 * @description:佣金監聽器
 * @author: Chen
 * @create: 2019-04-21 23:50
 **/
@Component
public class CommissionListener implements ApplicationListener {

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof PayEvent){
            System.out.println("有人下單,開始分傭啦");
        }
        System.out.println("收到廣播:MsgListener");
    }
}
/**
 * @description:信息監聽器
 * @author: Chen
 * @create: 2019-04-21 09:57
 **/
@Component
public class MsgListener  implements ApplicationListener {

    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        if (applicationEvent instanceof PayEvent){
            System.out.println("通知管理員,有人下單了");
        }
        System.out.println("收到廣播:MsgListener");
    }
}
/**
 * @description: 訂單監聽器
 * @author: Chen
 * @create: 2019-04-21 23:48
 **/
@Component
public class OrderListener implements ApplicationListener {

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof PayEvent){
            System.out.println("把訂單狀態改爲已支付");
        }
        System.out.println("收到廣播:OrderListener");
    }
}

(4)事件發佈者繼承ApplicationContextAware

/**
 * @description:發佈者
 * @author: Chen
 * @create: 2019-04-21 10:28
 **/
@Component
public class EventPublisher implements ApplicationContextAware {

    private ApplicationContext applicationContext;


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }

    public void publishEvent(ApplicationEvent event){
        applicationContext.publishEvent(event);
    }
}

(5)測試入口

@RestController
public class TestController {

    @Autowired
    private EventPublisher eventListener;

    @RequestMapping("pay")
    public String pay(){
        PayEvent payEvent = new PayEvent("訂單支付成功。");
        eventListener.publishEvent(payEvent);
        return "success";
    }

}

結果:

 

源碼:springboot中的發佈/訂閱範式 

相關文章
相關標籤/搜索