1. 程式人生 > >觀察者模式、發布訂閱和事件驅動

觀察者模式、發布訂閱和事件驅動

中間件 說明 發布訂閱 ret dem 天下 參與者 void setname

  觀察者模式(有時又被稱為模型(Model)-視圖(View)模式、源-收聽者(Listener)模式或從屬者模式)是軟件設計模式的一種。在此種模式中,一個目標物件管理所有相依於它的觀察者物件,並且在它本身的狀態改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實現。此種模式通常被用來實現事件處理系統。

  觀察者模式(Observer)完美的將觀察者和被觀察的對象分離開,以明星和粉絲舉例子,明星就是被觀察著,粉絲就是觀察者。下面是一個demo

package Observer;

public interface Observer {

    void update(Observable o);
}
package Observer;

import java.util.ArrayList;
import java.util.List;

public class Observable {

    private List<Observer> list = new ArrayList<>();
    public void addObserver(Observer o) {
        list.add(o);
    }
    private String status;
    
    public List<Observer> getList() {
        
return list; } public String getStatus() { return status; } public void movelUp (String status) { System.out.println(status); this.status = status; list.stream().forEach(o -> o.update(this)); } }
package Observer;

public class Client {

    
public static void main(String[] args) { Observable ob = new Observable(); ob.addObserver(new ObserverImpl("小花")); ob.addObserver(new ObserverImpl("小名")); ob.movelUp("天下無賊"); } }

  以上便是標準的觀察者模式,在被觀察著中維護了一個觀察者的列表,需要由被觀察者去完成註冊觀察者的功能,不符合單一職責原則。所以我們需要添加一個manager類,來方便用戶自己去訂閱某個明星。改造代碼如下:

package Observer2;

import java.util.ArrayList;
import java.util.List;

public class Star {

    private String name;

    private String lastMovel;

    // 標識被觀察者是否變化
    private boolean changed = false;

    private List<Observer> list = new ArrayList<>();

    public void addObserver(Observer o) {
        list.add(o);
    }

    public List<Observer> getList() {
        return list;
    }

    public void notifyObserver() {
        if (!changed) {
            return;
        }
        setChanged(false);
        list.stream().forEach(o -> o.update(this, null));
    }

    public boolean isChanged() {
        return changed;
    }

    public void setChanged(boolean changed) {
        this.changed = changed;
    }

    public void setList(List<Observer> list) {
        this.list = list;
    }

    public Star(String name) {
        super();
        this.name = name;
        Manager.getInstance().addStar(this);
    }

    
    public void movelUp(String movel) {
        System.out.println(name + "發布了" + movel);
        this.lastMovel = movel;
        setChanged(true);
        notifyObserver();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getLastMovel() {
        return lastMovel;
    }

    public void setLastMovel(String lastMovel) {
        this.lastMovel = lastMovel;
    }

}
package Observer2;

public interface Observer {

    void update(Star o,Object args);
}
package Observer2;

public class Fans implements Observer {
    
    private String name;
    
    

    public Fans(String name) {
        super();
        this.name = name;
    }



    public String getName() {
        return name;
    }



    public void setName(String name) {
        this.name = name;
    }


    public void subscribe(String starName) {
        Manager.getInstance().getStar(starName).addObserver(this);
    }

    @Override
    public void update(Star star,Object args) {
        System.out.println(name + "得到了" + star.getName() +"發布" + star.getLastMovel() + "的消息");
    }

    
}
package Observer2;

import java.util.HashMap;
import java.util.Map;

public class Manager {

    private Map<String, Star> observableMap = new HashMap<>();
    
    private Manager() {
        
    }
    public void addStar(Star star) {
        observableMap.put(star.getName(), star);
    }
    public Star getStar(String name) {
        return observableMap.get(name);
    }
    public static Manager getInstance() {
        return SingleManager.manager;
    }
    private static class SingleManager{
        private static Manager manager = new Manager();
        
    }
    
    
}
package Observer2;

public class Client {

    public static void main(String[] args) {
        Fans fans1 = new Fans("小明");
        Fans fans2 = new Fans("小話");
        Fans fans3 = new Fans("小張");
        Fans fans4 = new Fans("小裏");
        Star star1 = new Star("麗影");
        Star star2 = new Star("冪冪");
        fans1.subscribe("麗影");
        fans3.subscribe("冪冪");
        fans4.subscribe("冪冪");
        star1.movelUp("乘風破浪");
        star2.movelUp("孤島驚魂");
        System.out.println("----------------------------");
        fans2.subscribe("麗影");
        star1.movelUp("女兒國");
    }
}

  發布/訂閱模式(Pub/Sub)是一種消息模式,它有 兩個參與者 : 發布者和訂閱者 。發布者向 某個信道發布一條消息,訂閱者綁定這個信道,當有消息發布至信道時就會 接收到一個通知。最重要的一點是, 發布者和訂閱者是完全解耦的,彼此並不知曉對方的存在。從定義上可以看出,發布訂閱模式裏雙方是完全解耦的,而在觀察者模式裏,目標對象管理這觀察者,雙方是耦合的,這是最主要的區別,而在發布訂閱模式中多了一個中間層信道。我們常用的activeMQ、rabbitMQ、kafka等中間件就是為發布訂閱模式服務的。

  還有一種和觀察者模式很像的就是事件驅動模型,相信各位都知道tomcat,在使用的過程中,或許經常會有人用到listener,即監聽器這個概念。那麽其實這個就是一個事件驅動模型的應用。比如我們的spring,我們在應用啟動的時候要初始化我們的IOC容器,那麽我們的做法就是加入一個listener,這樣伴隨著tomcat服務器的啟動,spring的IOC容器就會跟著啟動。那麽這個listener其實就是事件驅動模型中的監聽器,它用來監聽它所感興趣的事,比如我們springIOC容器啟動的監聽器,就是實現的ServletContextListener這個接口,說明它對servletContext感興趣,會監聽servletContext的啟動和銷毀。事件驅動模型與觀察者模式勉強的對應關系可以看成是,被觀察者相當於事件源,觀察者相當於監聽器,事件源會產生事件,監聽器監聽事件。所以這其中就攙和到四個類,事件源,事件,監聽器以及具體的監聽器。下面我們就將上面的例子改造為事件驅動模型。

package Observer3;

import java.util.EventObject;

public class MovelEvent extends EventObject {

    private static final long serialVersionUID = -1231609728871248531L;

    public MovelEvent(Star star) {
        super(star);
    }

    public Star getStar() {
        return (Star) super.getSource();
    }
}
package Observer3;

import java.util.ArrayList;
import java.util.List;

public class Star {
    // 標識被觀察者是否變化
    private boolean changed = false;

    private List<MovelListener> list = new ArrayList<>();

    public void addMovelListener(MovelListener o) {
        list.add(o);
    }

    public List<MovelListener> getList() {
        return list;
    }

    public void setList(List<MovelListener> list) {
        this.list = list;
    }

    public void notifyObserver() {
        if (!changed) {
            return;
        }
        setChanged(false);
        MovelEvent movelEvent = new MovelEvent(this);
        list.stream().forEach(o -> o.update(movelEvent));
    }

    public boolean isChanged() {
        return changed;
    }

    public void setChanged(boolean changed) {
        this.changed = changed;
    }


    public Star(String name) {
        super();
        this.name = name;
        Manager.getInstance().addStar(this);
    }

    private String name;

    private String lastMovel;

    public void movelUp(String movel) {
        System.out.println(name + "發布了" + movel);
        this.lastMovel = movel;
        setChanged(true);
        notifyObserver();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getLastMovel() {
        return lastMovel;
    }

    public void setLastMovel(String lastMovel) {
        this.lastMovel = lastMovel;
    }

}
package Observer3;

import java.util.EventListener;

public interface MovelListener extends EventListener {
    
    void update (MovelEvent movelEvent);

}
package Observer3;

public class Fans implements MovelListener {
    
    private String name;
    
    

    public Fans(String name) {
        super();
        this.name = name;
    }



    public String getName() {
        return name;
    }



    public void setName(String name) {
        this.name = name;
    }


    public void subscribe(String starName) {
        Manager.getInstance().getStar(starName).addMovelListener(this);
    }

    @Override
    public void update(MovelEvent movelEvent) {
        Star star = movelEvent.getStar();
        System.out.println(name + "得到了" + star.getName() +"發布" + star.getLastMovel() + "的消息");
        
    }

    
}

   我們徹底將剛才的觀察者模式改成了事件驅動,現在我們使用事件驅動的類再運行一下客戶端,其中客戶端代碼和WriterManager類的代碼是完全不需要改動的,直接運行客戶端即可。我們會發現得到的結果與觀察者模式一模一樣。

  那麽觀察者模式和事件驅動的不同之處在哪裏呢?首先從實現方式上就能看出,事件驅動可以解決觀察者模式的問題,但反過來則不一定,另外二者所表達的業務場景也不一樣,比如上述例子,使用觀察者模式更貼近業務場景的描述,而使用事件驅動,從業務上講,則有點勉強。再者從功能上講,觀察者模式中觀察者的響應理論上講針對特定的被觀察者是唯一的(當然如果在update方法中ifelse則也能實現對多個被觀察著);事件驅動則更靈活,可以定義自己感興趣的事情,比如我們可以監聽movel,也可以在接口裏加一個singListener的接口,然後我們的fans同時實現這個接口,那麽star在發布singEvent的時候,只要實現了singListener的類的實例就可以收到消息,當然程序的復雜性就增加了。

  最後總結一下,觀察者模式和發布訂閱模型的區別就在於消息是否發送給中間件,觀察者和被觀察著是否完全解耦;而觀察者模式和事件驅動的區別則在於事件驅動則更加靈活,但同時增加了程序的復雜性。

  

觀察者模式、發布訂閱和事件驅動