基於Guava API實現異步通知和事件回調


本文節選自《設計模式就該這樣學》

1 基於Java API實現通知機制

當小伙伴們在社區提問時,如果有設置指定用戶回答,則對應的用戶就會收到郵件通知,這就是觀察者模式的一種應用場景。有些小伙伴可能會想到MQ、異步隊列等,其實JDK本身就提供這樣的API。我們用代碼來還原這樣一個應用場景,首先創建GPer類。


/**
 * JDK提供的一種觀察者的實現方式,被觀察者
 */
public class GPer extends Observable{
    private String name = "GPer生態圈";
    private static GPer gper = null;
    private GPer(){}

    public static GPer getInstance(){
        if(null == gper){
            gper = new GPer();
        }
        return gper;
    }
    public String getName() {
        return name;
    }
    public void publishQuestion(Question question){
        System.out.println(question.getUserName() + "在" + this.name + "上提交了一個問題。");
        setChanged();
        notifyObservers(question);
    }
}

然后創建問題Question類。


public class Question {
    private String userName;
    private String content;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

接着創建老師Teacher類。


public class Teacher implements Observer {

    private String name;

    public Teacher(String name) {
        this.name = name;
    }

    public void update(Observable o, Object arg) {
        GPer gper = (GPer)o;
        Question question = (Question)arg;
        System.out.println("======================");
        System.out.println(name + "老師,你好!\n" + 
"您收到了一個來自" + gper.getName() + "的提問,希望您解答。問題內容如下:\n" +
                   question.getContent() + "\n" + "提問者:" + question.getUserName());
    }
}

最后編寫客戶端測試代碼。


    public static void main(String[] args) {
        GPer gper = GPer.getInstance();
        Teacher tom = new Teacher("Tom");
        Teacher jerry = new Teacher("Jerry");

        gper.addObserver(tom);
        gper.addObserver(jerry);

        //用戶行為
        Question question = new Question();
        question.setUserName("張三");
        question.setContent("觀察者模式適用於哪些場景?");

        gper.publishQuestion(question);
}

運行結果如下圖所示。

file

2 基於Guava API輕松落地觀察者模式

筆者向大家推薦一個實現觀察者模式的非常好用的框架,API使用也非常簡單,舉個例子,首先引入Maven依賴包。


<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>20.0</version>
</dependency>

然后創建偵聽事件GuavaEvent。


/**
 * Created by Tom
 */
public class GuavaEvent {
    @Subscribe
    public void subscribe(String str){
        //業務邏輯
        System.out.println("執行subscribe方法,傳入的參數是:" + str);
    }

}

最后編寫客戶端測試代碼。



/**
 * Created by Tom
 */
public class GuavaEventTest {
    public static void main(String[] args) {
        EventBus eventbus = new EventBus();
        GuavaEvent guavaEvent = new GuavaEvent();
        eventbus.register(guavaEvent);
        eventbus.post("Tom");
    }

}

3 使用觀察者模式設計鼠標事件響應API

再來設計一個業務場景,幫助小伙伴們更好地理解觀察者模式。在JDK源碼中,觀察者模式的應用也非常多。例如java.awt.Event就是觀察者模式的一種,只不過Java很少被用來寫桌面程序。我們用代碼來實現一下,以幫助小伙伴們更深刻地了解觀察者模式的實現原理。首先,創建EventListener接口。


/**
 * 觀察者抽象
 * Created by Tom.
 */
public interface EventListener {

}

創建Event類。



/**
 * 標准事件源格式的定義
 * Created by Tom.
 */
public class Event {
    //事件源,動作是由誰發出的
    private Object source;
    //事件觸發,要通知誰(觀察者)
    private EventListener target;
    //觀察者的回應
    private Method callback;
    //事件的名稱
    private String trigger;
    //事件的觸發事件
    private long time;

    public Event(EventListener target, Method callback) {
        this.target = target;
        this.callback = callback;
    }

    public Object getSource() {
        return source;
    }

    public Event setSource(Object source) {
        this.source = source;
        return this;
    }

    public String getTrigger() {
        return trigger;
    }

    public Event setTrigger(String trigger) {
        this.trigger = trigger;
        return this;
    }

    public long getTime() {
        return time;
    }

    public Event setTime(long time) {
        this.time = time;
        return this;
    }

    public Method getCallback() {
        return callback;
    }

    public EventListener getTarget() {
        return target;
    }

    @Override
    public String toString() {
        return "Event{" +
                "source=" + source +
                ", target=" + target +
                ", callback=" + callback +
                ", trigger='" + trigger + '\'' +
                ", time=" + time +
                '}';
    }
}

創建EventContext類。


/**
 * 被觀察者的抽象
 * Created by Tom.
 */
public abstract class EventContext {
    protected Map<String,Event> events = new HashMap<String,Event>();

    public void addListener(String eventType, EventListener target, Method callback){
        events.put(eventType,new Event(target,callback));
    }

    public void addListener(String eventType, EventListener target){
        try {
            this.addListener(eventType, target, 
target.getClass().getMethod("on"+toUpperFirstCase(eventType), Event.class));
        }catch (NoSuchMethodException e){
            return;
        }
    }

    private String toUpperFirstCase(String eventType) {
        char [] chars = eventType.toCharArray();
        chars[0] -= 32;
        return String.valueOf(chars);
    }

    private void trigger(Event event){
        event.setSource(this);
        event.setTime(System.currentTimeMillis());

        try {
            if (event.getCallback() != null) {
                //用反射調用回調函數
                event.getCallback().invoke(event.getTarget(), event);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    protected void trigger(String trigger){
        if(!this.events.containsKey(trigger)){return;}
        trigger(this.events.get(trigger).setTrigger(trigger));
    }
}

然后創建MouseEventType接口。


/**
 * Created by Tom.
 */
public interface MouseEventType {
    //單擊
    String ON_CLICK = "click";

    //雙擊
    String ON_DOUBLE_CLICK = "doubleClick";

    //彈起
    String ON_UP = "up";

    //按下
    String ON_DOWN = "down";

    //移動
    String ON_MOVE = "move";

    //滾動
    String ON_WHEEL = "wheel";

    //懸停
    String ON_OVER = "over";

    //失去焦點
    String ON_BLUR = "blur";

    //獲得焦點
    String ON_FOCUS = "focus";
}

創建Mouse類。


/**
 * 具體的被觀察者
 * Created by Tom.
 */
public class Mouse extends EventContext {

    public void click(){
        System.out.println("調用單擊方法");
        this.trigger(MouseEventType.ON_CLICK);
    }

    public void doubleClick(){
        System.out.println("調用雙擊方法");
        this.trigger(MouseEventType.ON_DOUBLE_CLICK);
    }

    public void up(){
        System.out.println("調用彈起方法");
        this.trigger(MouseEventType.ON_UP);
    }

    public void down(){
        System.out.println("調用按下方法");
        this.trigger(MouseEventType.ON_DOWN);
    }

    public void move(){
        System.out.println("調用移動方法");
        this.trigger(MouseEventType.ON_MOVE);
    }

    public void wheel(){
        System.out.println("調用滾動方法");
        this.trigger(MouseEventType.ON_WHEEL);
    }

    public void over(){
        System.out.println("調用懸停方法");
        this.trigger(MouseEventType.ON_OVER);
    }

    public void blur(){
        System.out.println("調用獲得焦點方法");
        this.trigger(MouseEventType.ON_BLUR);
    }

    public void focus(){
        System.out.println("調用失去焦點方法");
        this.trigger(MouseEventType.ON_FOCUS);
    }
}

創建回調方法MouseEventLisenter類。


/**
 * 觀察者
 * Created by Tom.
 */
public class MouseEventListener implements EventListener {


    public void onClick(Event e){
        System.out.println("===========觸發鼠標單擊事件==========" + "\n" + e);
    }

    public void onDoubleClick(Event e){
        System.out.println("===========觸發鼠標雙擊事件==========" + "\n" + e);
    }

    public void onUp(Event e){
        System.out.println("===========觸發鼠標彈起事件==========" + "\n" + e);
    }

    public void onDown(Event e){
        System.out.println("===========觸發鼠標按下事件==========" + "\n" + e);
    }

    public void onMove(Event e){
        System.out.println("===========觸發鼠標移動事件==========" + "\n" + e);
    }

    public void onWheel(Event e){
        System.out.println("===========觸發鼠標滾動事件==========" + "\n" + e);
    }

    public void onOver(Event e){
        System.out.println("===========觸發鼠標懸停事件==========" + "\n" + e);
    }

    public void onBlur(Event e){
        System.out.println("===========觸發鼠標失去焦點事件==========" + "\n" + e);
    }

    public void onFocus(Event e){
        System.out.println("===========觸發鼠標獲得焦點事件==========" + "\n" + e);
    }

}

最后編寫客戶端測試代碼。


    public static void main(String[] args) {
        EventListener listener = new MouseEventListener();

        Mouse mouse = new Mouse();
        mouse.addListener(MouseEventType.ON_CLICK,listener);
        mouse.addListener(MouseEventType.ON_MOVE,listener);

        mouse.click();
        mouse.move();
    }
		

關注微信公眾號『 Tom彈架構 』回復“設計模式”可獲取完整源碼。

【推薦】Tom彈架構:30個設計模式真實案例(附源碼),挑戰年薪60W不是夢

本文為“Tom彈架構”原創,轉載請注明出處。技術在於分享,我分享我快樂!
如果本文對您有幫助,歡迎關注和點贊;如果您有任何建議也可留言評論或私信,您的支持是我堅持創作的動力。關注微信公眾號『 Tom彈架構 』可獲取更多技術干貨!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM