国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

香港云服务器
服務器之家 - 編程語言 - Java教程 - Java設計模式之java觀察者模式詳解

Java設計模式之java觀察者模式詳解

2022-01-04 13:06大忽悠愛忽悠 Java教程

這篇文章主要介紹了Java經典設計模式之觀察者模式,簡單分析了觀察者模式的概念、原理并結合實例形式給出了java觀察者模式的具體用法與相關注意事項,需要的朋友可以參考下

 

引言

觀察者模式是設計模式中的 “超級模式”,其應用隨處可見,我們以微信公眾號為例。

微信公眾號有服務號、訂閱號和企業號之分。當我們在公眾號上發布一篇博文推送時,訂閱的用戶都能夠在我發布推送之后及時接收到推送,即可方便地在手機端進行閱讀。

Java設計模式之java觀察者模式詳解

 

介紹

觀察者模式(Observer Pattern):定義對象之間的一種一對多依賴關系,使得每當一個對象狀態發生改變時,其相關依賴對象皆得到通知并被自動更新。觀察者模式是一種對象行為型模式。

觀察者模式的別名包括發布-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監聽器(Source/Listener)模式或從屬者(Dependents)模式。

觀察者模式包含觀察目標和觀察者兩類對象,**一個目標可以有任意數目的與之相依賴的觀察者,**一旦觀察目標的狀態發生改變,所有的觀察者都將得到通知。

  • 一般是多對一依賴,即一個被觀察者,和多個觀察者
  • 一旦大忽悠更新了公眾號,所有訂閱其公眾號的粉絲都會接收到更新推送

 

角色

Subject(目標):目標又稱為主題,它是指被觀察的對象。在目標中定義了一個觀察者集合,一個觀察目標可以接受任意數量的觀察者來觀察,它提供一系列方法來增加和刪除觀察者對象,同時它定義了通知方法notify()。目標類可以是接口,也可以是抽象類或具體類。

ConcreteSubject(具體目標):具體目標是目標類的子類,通常它包含有經常發生改變的數據,當它的狀態發生改變時,向它的各個觀察者發出通知;同時它還實現了在目標類中定義的抽象業務邏輯方法(如果有的話)。如果無須擴展目標類,則具體目標類可以省略。

Observer(觀察者):觀察者將對觀察目標的改變做出反應,觀察者一般定義為接口,該接口聲明了更新數據的方法update(),因此又稱為抽象觀察者。

ConcreteObserver(具體觀察者):在具體觀察者中維護一個指向具體目標對象的引用,它存儲具體觀察者的有關狀態,這些狀態需要和具體目標的狀態保持一致;它實現了在抽象觀察者Observer中定義的update()方法。通常在實現時,可以調用具體目標類的attach()方法將自己添加到目標類的集合中或通過detach()方法將自己從目標類的集合中刪除。

 

原理類圖

Java設計模式之java觀察者模式詳解

 

微信訂閱號的案例

首先需要一個訂閱者接口(觀察者),該接口有一個 receive 方法,用于接收公眾號推送通知

//訂閱者---觀察者
public interface Subscriber
{
    //接收發布者發布消息的方法
    public void receive();
}

然后是一個微信客戶端(具體觀察者),實現了 receive 方法

//處理微信訂閱的業務邏輯
public class WeChatSub implements Subscriber
{
    //當前訂閱者的名字
    private String subName;
    WeChatSub(String subName)
    {
        this.subName=subName;
    }
    @Override
    public void receive(String publisher, String passageName) {
       //接收到推送消息時的具體業務邏輯操作
        System.out.println(String.format("用戶[%s] ,  接收到[%s]的訂閱號推送," +
                "推送文章為:%s ",subName,publisher,passageName));
    }
}

發布者類(目標,被觀察對象),該類維護了一個訂閱者列表,實現了訂閱、取消訂閱、通知所有訂閱者等功能

//發布者--被觀察的對象Subject
public class Publisher
{
    //存放所有需要通知的觀察者
   static private List<Subscriber> subscribers=new ArrayList<>();
   //記錄是否發布的狀態,默認false
    static private  Boolean pubStatus=false;
    protected void subscribe(Subscriber subscriber) {
        this.subscribers.add(subscriber);
    }
    protected void unsubscribe(Subscriber subscriber) {
        if (this.subscribers.contains(subscriber)) {
            this.subscribers.remove(subscriber);
        }
    }
    protected void notifySubscribers(String publisher, String articleName) {
        if (this.pubStatus == false) {
            return;
        }
        for (Subscriber subscriber : this.subscribers) {
            subscriber.receive(publisher, articleName);
        }
        this.clearPubStatus();
    }
    protected void setPubStatus() {
        this.pubStatus = true;
    }
    protected void clearPubStatus() {
        this.pubStatus = false;
    }
}

微信公眾號類(具體目標),該類提供了 publishArticles 方法,用于發布推送,當文章發布完畢時調用父類的通知所有訂閱者方法

public class WeChatPublisher extends  Publisher
{
    private String name;
    public WeChatPublisher(String name) {
        this.name = name;
    }
    public void publishArticles(String articleName, String content) {
        System.out.println(String.format("
<%s>微信公眾號 發布了一篇推送,文章名稱為 <%s>,內容為 <%s> ", this.name, articleName, content));
        setPubStatus();
        notifySubscribers(this.name, articleName);
    }
}

客戶端測試

//客戶端
public class Client
{
    public static void main(String[] args) {
        //創建一個發布者
        WeChatPublisher dhy=new WeChatPublisher("大忽悠");
        //創建訂閱者
        Subscriber sub1=new WeChatSub("小朋友");
        Subscriber sub2=new WeChatSub("小忽悠");
        Subscriber sub3=new WeChatSub("大朋友");
        //訂閱大忽悠公眾號
        dhy.subscribe(sub1);
        dhy.subscribe(sub2);
        dhy.subscribe(sub3);
        //大忽悠發布推送
        dhy.publishArticles("設計模式","觀察者模式");
    }
}

Java設計模式之java觀察者模式詳解

 

 

總結

 

優點

  • 觀察者模式可以實現表示層和數據邏輯層的分離,定義了穩定的消息更新傳遞機制,并抽象了更新接口,使得可以有各種各樣不同的表示層充當具體觀察者角色。
  • 觀察者模式在觀察目標和觀察者之間建立一個抽象的耦合。觀察目標只需要維持一個抽象觀察者的集合,無須了解其具體觀察者。由于觀察目標和觀察者沒有緊密地耦合在一起,因此它們可以屬于不同的抽象化層次。
  • 觀察者模式支持廣播通信,觀察目標會向所有已注冊的觀察者對象發送通知,簡化了一對多系統設計的難度。
  • 觀察者模式滿足 “開閉原則”的要求,增加新的具體觀察者無須修改原有系統代碼,在具體觀察者與觀察目標之間不存在關聯關系的情況下,增加新的觀察目標也很方便。

 

缺點

  • 如果一個觀察目標對象有很多直接和間接觀察者,將所有的觀察者都通知到會花費很多時間。
  • 如果在觀察者和觀察目標之間存在循環依賴,觀察目標會觸發它們之間進行循環調用,可能導致系統崩潰。
  • 觀察者模式沒有相應的機制讓觀察者知道所觀察的目標對象是怎么發生變化的,而僅僅只是知道觀察目標發生了變化。

 

適用場景

  • 一個抽象模型有兩個方面,其中一個方面依賴于另一個方面,將這兩個方面封裝在獨立的對象中使它們可以各自獨立地改變和復用。
  • 一個對象的改變將導致一個或多個其他對象也發生改變,而并不知道具體有多少對象將發生改變,也不知道這些對象是誰。
  • 需要在系統中創建一個觸發鏈,A對象的行為將影響B對象,B對象的行為將影響C對象……,可以使用觀察者模式創建一種鏈式觸發機制。

 

觀察者模式的典型應用

 

JDK 提供的觀察者接口

觀察者模式在Java語言中的地位非常重要。在JDK的 java.util 包中,提供了 Observable 類以及 Observer 接口,它們構成了JDK對觀察者模式的支持。

其中的 Observer 接口為觀察者,只有一個 update 方法,當觀察目標發生變化時被調用,其代碼如下:

public interface Observer {
    void update(Observable o, Object arg);
}

Observable 類則為目標類,相比我們的示例中的 Publisher 類多了并發和NPE方面的考慮

public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs = new Vector();
    public Observable() {
    }
    // 用于注冊新的觀察者對象到向量中
    public synchronized void addObserver(Observer var1) {
        if (var1 == null) {
            throw new NullPointerException();
        } else {
            if (!this.obs.contains(var1)) {
                this.obs.addElement(var1);
            }
        }
    }
    // 用于刪除向量中的某一個觀察者對象
    public synchronized void deleteObserver(Observer var1) {
        this.obs.removeElement(var1);
    }
    public void notifyObservers() {
        this.notifyObservers((Object)null);
    }
    // 通知方法,用于在方法內部循環調用向量中每一個觀察者的update()方法
    public void notifyObservers(Object var1) {
        Object[] var2;
        synchronized(this) {
            if (!this.changed) {
                return;
            }
            var2 = this.obs.toArray();
            this.clearChanged();
        }
        for(int var3 = var2.length - 1; var3 >= 0; --var3) {
            ((Observer)var2[var3]).update(this, var1);
        }
    }
    // 用于清空向量,即刪除向量中所有觀察者對象
    public synchronized void deleteObservers() {
        this.obs.removeAllElements();
    }
    // 該方法被調用后會設置一個boolean類型的內部標記變量changed的值為true,表示觀察目標對象的狀態發生了變化
    protected synchronized void setChanged() {
        this.changed = true;
    }
    // 用于將changed變量的值設為false,表示對象狀態不再發生改變或者已經通知了所有的觀察者對象,調用了它們的update()方法
    protected synchronized void clearChanged() {
        this.changed = false;
    }
    // 返回對象狀態是否改變
    public synchronized boolean hasChanged() {
        return this.changed;
    }
    // 返回向量中觀察者的數量
    public synchronized int countObservers() {
        return this.obs.size();
    }
}

我們可以使用 Observable 類以及 Observer 接口來重新實現微信公眾號示例。

增加一個通知類 WechatNotice,用于推送通知的傳遞

@Data
@AllArgsConstructor
public class WechatNotice {
    private String publisher;
    private String articleName;
}

然后改寫 WeChatClient WeChatAccounts,分別實現JDK的 Observer 接口和繼承 Observable

public class WeChatClient implements Observer {
    private String username;
    public WeChatClient(String username) {
        this.username = username;
    }
    @Override
    public void update(Observable o, Object arg) {
        //WeChatAccounts weChatAccounts = (WeChatAccounts) o;
        WechatNotice notice = (WechatNotice) arg;
        System.out.println(String.format("用戶<%s> 接收到 <%s>微信公眾號 的推送,文章標題為 <%s>", username, notice.getPublisher(), notice.getArticleName()));
    }
}
public class WeChatAccounts extends Observable {
    private String name;
    public WeChatAccounts(String name) {
        this.name = name;
    }
    public void publishArticles(String articleName, String content) {
        System.out.println(String.format("
<%s>微信公眾號 發布了一篇推送,文章名稱為 <%s>,內容為 <%s> ", this.name, articleName, content));
        setChanged();
        notifyObservers(new WechatNotice(this.name, articleName));
    }
}

測試,與示例中的測試代碼的區別在于調用的方法不同

public class Test {
    public static void main(String[] args) {
        WeChatAccounts accounts = new WeChatAccounts("大忽悠");
        WeChatClient user1 = new WeChatClient("張三");
        WeChatClient user2 = new WeChatClient("李四");
        WeChatClient user3 = new WeChatClient("王五");
        accounts.addObserver(user1);
        accounts.addObserver(user2);
        accounts.addObserver(user3);
        accounts.publishArticles("設計模式 | 觀察者模式及典型應用", "觀察者模式的內容...");
        accounts.deleteObserver(user1);
        accounts.publishArticles("設計模式 | 單例模式及典型應用", "單例模式的內容....");
    }
}

 

Guava EventBus 中的觀察者模式

Guava 中的 EventBus 封裝了友好的 “生產/消費模型”,通過非常簡單的方式,實現了觀察者模式中的監聽注冊,事件分發。

使用了 Guava EventBus 之后,如果需要訂閱消息,不需要實現任何接口,只需在監聽方法上加上 @Subscribe 注解即可,EventBus 提供了 registerunregister 方法用于注冊與取消注冊事件,當 EventBus 調用 post 方法時將把事件分發給注冊的對象

使用 Guava 重新實現示例

@Data
@AllArgsConstructor
public class WechatNotice {
    private String publisher;
    private String articleName;
}
public class WeChatClient  {
    private String username;
    public WeChatClient(String username) {
        this.username = username;
    }
    @Subscribe
    public void listen(WechatNotice notice) {
        System.out.println(String.format("用戶<%s> 接收到 <%s>微信公眾號 的推送,文章標題為 <%s>", username, notice.getPublisher(), notice.getArticleName()));
    }
}
public class WeChatAccounts {
    private String name;
    private EventBus eventBus;
    public WeChatAccounts(String name) {
        this.name = name;
        this.eventBus = new EventBus();
    }
    public void publishArticles(String articleName, String content) {
        System.out.println(String.format("
<%s>微信公眾號 發布了一篇推送,文章名稱為 <%s>,內容為 <%s> ", this.name, articleName, content));
        //post方法將會將被派發的消息,傳遞給所有的訂閱者,并調用訂閱者的監聽方法
        this.eventBus.post(new WechatNotice(this.name, articleName));
    }
    public void register(WeChatClient weChatClient) {
        this.eventBus.register(weChatClient);
    }
    public void unregister(WeChatClient weChatClient) {
        this.eventBus.unregister(weChatClient);
    }
}

測試

public class Test {
    public static void main(String[] args) {
        WeChatAccounts accounts = new WeChatAccounts("小旋鋒");
        WeChatClient user1 = new WeChatClient("張三");
        WeChatClient user2 = new WeChatClient("李四");
        WeChatClient user3 = new WeChatClient("王五");
        accounts.register(user1);
        accounts.register(user2);
        accounts.register(user3);
        accounts.publishArticles("設計模式 | 觀察者模式及典型應用", "觀察者模式的內容...");
        accounts.unregister(user1);
        accounts.publishArticles("設計模式 | 單例模式及典型應用", "單例模式的內容....");
    }
}

 

Spring ApplicationContext 事件機制中的觀察者模式

spring的事件機制是從java的事件機制拓展而來,ApplicationContext 中事件處理是由 ApplicationEvent 類和 ApplicationListener 接口來提供的。如果一個Bean實現了 ApplicationListener 接口,并且已經發布到容器中去,每次 ApplicationContext 發布一個 ApplicationEvent 事件,這個Bean就會接到通知

  • ApplicationContext:事件源,其中的 publishEvent()方法用于觸發容器事件
  • ApplicationEvent:事件本身,自定義事件需要繼承該類,可以用來傳遞數據
  • ApplicationListener:事件監聽器接口,事件的業務邏輯封裝在監聽器里面
@Data
public class WechatNotice extends ApplicationEvent {
    private String publisher;
    private String articleName;
    public WechatNotice(Object source, String publisher, String articleName) {
        super(source);
        this.publisher = publisher;
        this.articleName = articleName;
    }
}
public class WeChatClient implements ApplicationListener {
    private String username;
    public WeChatClient(String username) {
        this.username = username;
    }
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof WechatNotice) {
            WechatNotice notice = (WechatNotice) event;
            System.out.println(String.format("用戶<%s> 接收到 <%s>微信公眾號 的推送,文章標題為 <%s>", username, notice.getPublisher(), notice.getArticleName()));
        }
    }
    public void setUsername(String username) {
        this.username = username;
    }
}

 

public class WeChatAccounts implements ApplicationContextAware {
    private ApplicationContext ctx;
    private String name;
    public WeChatAccounts(String name) {
        this.name = name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.ctx = applicationContext;
    }
    public void publishArticles(String articleName, String content) {
        System.out.println(String.format("
<%s>微信公眾號 發布了一篇推送,文章名稱為 <%s>,內容為 <%s> ", this.name, articleName, content));
        ctx.publishEvent(new WechatNotice(this.name, this.name, articleName));
    }
}

在 resources 目錄下創建 spring.xml 文件,填入下面的內容

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="WeChatAccounts" class="com.observer.sprintevent.WeChatAccounts" scope="prototype">
        <constructor-arg name="name" value=""></constructor-arg>
    </bean>
    <bean id="WeChatClient1" class="com.observer.sprintevent.WeChatClient">
        <constructor-arg name="username" value="張三"></constructor-arg>
    </bean>
    <bean id="WeChatClient2" class="com.observer.sprintevent.WeChatClient">
        <constructor-arg name="username" value="李四"></constructor-arg>
    </bean>
    <bean id="WeChatClient3" class="com.observer.sprintevent.WeChatClient">
        <constructor-arg name="username" value="王五"></constructor-arg>
    </bean>
</beans>

測試

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        WeChatAccounts accounts = (WeChatAccounts) context.getBean("WeChatAccounts");
        accounts.setName("大忽悠");
        accounts.setApplicationContext(context);
        accounts.publishArticles("設計模式 | 觀察者模式及典型應用", "觀察者模式的內容...");
    }
}

在此示例中 ApplicationContext 對象的實際類型為 ClassPathXmlApplicationContext,其中的與 publishEvent 方法相關的主要代碼如下:

private ApplicationEventMulticaster applicationEventMulticaster;
public void publishEvent(ApplicationEvent event) {
    this.getApplicationEventMulticaster().multicastEvent(event);
    if (this.parent != null) {
        this.parent.publishEvent(event);
    }
}
ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
    return this.applicationEventMulticaster;
}
protected void initApplicationEventMulticaster() {
        ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
        if (beanFactory.containsLocalBean("applicationEventMulticaster")) {
            this.applicationEventMulticaster = (ApplicationEventMulticaster)beanFactory.getBean("applicationEventMulticaster", ApplicationEventMulticaster.class);
        } else {
            this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
            beanFactory.registerSingleton("applicationEventMulticaster", this.applicationEventMulticaster);
        }
    }

其中的 SimpleApplicationEventMulticaster 如下,multicastEvent 方法主要是通過遍歷 ApplicationListener(注冊由 AbstractApplicationEventMulticaster 實現),使用線程池框架 Executor 來并發執行 ApplicationListener 的 onApplicationEvent 方法,與示例本質上是一致的

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
    private Executor taskExecutor;
    public void multicastEvent(final ApplicationEvent event) {
        Iterator var2 = this.getApplicationListeners(event).iterator();
        while(var2.hasNext()) {
            final ApplicationListener listener = (ApplicationListener)var2.next();
            Executor executor = this.getTaskExecutor();
            if (executor != null) {
                executor.execute(new Runnable() {
                    public void run() {
                        listener.onApplicationEvent(event);
                    }
                });
            } else {
                listener.onApplicationEvent(event);
            }
        }
    }
}

 

參考文章

springboot啟動源碼

設計模式 | 觀察者模式及典型應用

總結

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關注服務器之家的更多內容!

原文鏈接:https://blog.csdn.net/m0_53157173/article/details/120198876

延伸 · 閱讀

精彩推薦
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發現了對于集合操作轉換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關于Java8中S...

    阿杜7482021-02-04
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

    這篇文章主要介紹了xml與Java對象的轉換詳解的相關資料,需要的朋友可以參考下...

    Java教程網2942020-09-17
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩中求8032021-07-12
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經有好久沒有升過級了。升級完畢重啟之后,突然發現好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java BufferWriter寫文件寫不進去或缺失數據的解決

    Java BufferWriter寫文件寫不進去或缺失數據的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數據的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程Java實現搶紅包功能

    Java實現搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
1126
主站蜘蛛池模板: 中文字幕一区二区三区四区 | 精品伊人 | 天堂v在线视频 | 日本成人三级 | 色狠狠久久av五月综合 | 国产日韩精品一区 | 日韩成人精品在线 | 久久中文字幕一区 | 国产精品毛片一区视频播不卡 | 欧美日韩精品免费观看 | 91亚洲国产成人久久精品网站 | 黄色片网站视频 | 久久精品中文字幕 | 在线中文字幕视频 | 免费的一级毛片 | 精品9999| 日韩精品一区二区三区 | 免费观看黄色av网站 | 国产亚洲精品久久久久久久 | 欧美一区二区三区电影 | 欧美一级在线观看 | 国产精品福利视频 | 午夜小视频在线观看 | 毛片在线视频 | 91大神免费观看 | 毛片在线一区二区观看精品 | 免费日本视频 | 日本黄色一区 | 人人玩人人添人人澡97 | 日韩精品一区二区三区丰满 | 国产又色又爽又黄 | 久草热8精品视频在线观看 毛片黄片免费观看 | 欧美日本一区 | 亚洲福利影院 | 99久久国语露脸精品对白 | 欧美一区二区三区在线看 | 久久最新 | 天天躁日日躁bbbbb | 成人精品久久久 | 久久精品国产99国产精品 | 精品在线一区二区 |