自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一文搞懂設(shè)計模式—觀察者模式

開發(fā)
觀察者模式在許多場景下都非常有用,但在使用時需要注意性能問題、循環(huán)依賴和執(zhí)行順序等方面的考慮。

觀察者模式(Observer Pattern)是一種常見的行為型設(shè)計模式,用于在對象之間建立一種一對多的依賴關(guān)系。當(dāng)一個對象的狀態(tài)發(fā)生變化時,所有依賴它的對象都將得到通知并自動更新。

一、使用場景

觀察者模式在許多應(yīng)用中都有廣泛的應(yīng)用,特別是當(dāng)存在對象之間的一對多關(guān)系,并且需要實時通知和更新時,觀察者模式非常適用。下面列舉幾個典型的使用場景:

  • 消息發(fā)布/訂閱系統(tǒng):觀察者模式可以用于構(gòu)建消息發(fā)布/訂閱系統(tǒng),其中消息發(fā)布者充當(dāng)主題(被觀察者),而訂閱者則充當(dāng)觀察者。當(dāng)發(fā)布者發(fā)布新消息時,所有訂閱者都會收到通知并執(zhí)行相應(yīng)操作。
  • 用戶界面組件:在圖形用戶界面 (GUI) 開發(fā)中,觀察者模式常被用于處理用戶界面組件之間的交互。當(dāng)一個組件的狀態(tài)發(fā)生變化時,其他依賴該組件的組件將自動更新以反映新的狀態(tài)。
  • 股票市場監(jiān)控:在金融領(lǐng)域,觀察者模式可用于實現(xiàn)股票市場監(jiān)控系統(tǒng)。各個投資者可以作為觀察者訂閱感興趣的股票,在股票價格變動時即時收到通知。
  • 事件驅(qū)動系統(tǒng):觀察者模式也常用于事件驅(qū)動系統(tǒng)中,如圖形用戶界面框架、游戲引擎等。當(dāng)特定事件發(fā)生時,觸發(fā)相應(yīng)的回調(diào)函數(shù)并通知所有注冊的觀察者。

以上僅是觀察者模式的一些典型使用場景,實際上,只要存在對象之間的依賴關(guān)系,并且需要實現(xiàn)解耦和靈活性,觀察者模式都可以考慮作為一種設(shè)計方案。

二、實現(xiàn)方式

觀察者模式包含以下幾個核心角色:

  • 主題(Subject):也稱為被觀察者或可觀察者,它是具有狀態(tài)的對象,并維護著一個觀察者列表。主題提供了添加、刪除和通知觀察者的方法。
  • 觀察者(Observer):觀察者是接收主題通知的對象。觀察者需要實現(xiàn)一個更新方法,當(dāng)收到主題的通知時,調(diào)用該方法進行更新操作。
  • 具體主題(Concrete Subject):具體主題是主題的具體實現(xiàn)類。它維護著觀察者列表,并在狀態(tài)發(fā)生改變時通知觀察者。
  • 具體觀察者(Concrete Observer):具體觀察者是觀察者的具體實現(xiàn)類。它實現(xiàn)了更新方法,定義了在收到主題通知時需要執(zhí)行的具體操作。

下面是觀察者模式的經(jīng)典實現(xiàn)方式:

(1) 定義觀察者接口:創(chuàng)建一個名為 Observer 的接口,包含一個用于接收通知的方法,例如 update()。

public interface Observer {
    void update();
}

(2) 定義主題接口:創(chuàng)建一個名為 Subject 的接口,包含用于管理觀察者的方法,如 registerObserver()、removeObserver() 和 notifyObservers()。

public interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}

(3) 實現(xiàn)具體主題:創(chuàng)建一個具體類實現(xiàn) Subject 接口,實現(xiàn)注冊、移除和通知觀察者的方法。在狀態(tài)變化時調(diào)用 notifyObservers() 方法通知所有觀察者。

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

public class ConcreteSubject implements Subject {
    private final List<Observer> observers = new ArrayList<>();
    private int state;

    public void setState(int state) {
        this.state = state;
        notifyObservers();
    }

    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
}

(4) 實現(xiàn)具體觀察者:創(chuàng)建一個具體類實現(xiàn) Observer 接口,實現(xiàn)接收通知并進行相應(yīng)處理的方法。

public class ConcreteObserver implements Observer {
    private String name;
    private Subject subject;

    public ConcreteObserver(String name, Subject subject) {
        this.name = name;
        this.subject = subject;
    }

    @Override
    public void update() {
        System.out.println(name+" received notification");
    }
}

(5) 使用觀察者模式:在實際代碼中,我們可以創(chuàng)建具體的主題和觀察者對象,并進行注冊和觸發(fā)狀態(tài)變化。

public class Main {
    public static void main(String[] args) {
        ConcreteSubject subject = new ConcreteSubject();

        ConcreteObserver observer1 = new ConcreteObserver("Observer 1", subject);
        ConcreteObserver observer2 = new ConcreteObserver("Observer 2", subject);

        subject.registerObserver(observer1);
        subject.registerObserver(observer2);

        subject.setState(10);
        // Output:
        // Observer 1 received notification
        // Observer 2 received notification

        subject.removeObserver(observer1);

        subject.setState(20);
        // Output:
        // Observer 2 received notification
    }
}

1.Java對觀察者模式的支持

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

使用 Observable 類以及 Observer 接口,優(yōu)化之后的代碼為:

// 具體觀察者
public class ConcreteObserver implements Observer {
    private String name;

    public ConcreteObserver(String name) {
        // 設(shè)置每一個觀察者的名字
        this.name = name;
    }

    /**
     * 當(dāng)變化之后,就會自動觸發(fā)該方法
     */
    @Override
    public void update(Observable o, Object arg) {
        if (arg instanceof Integer) {
            System.out.println(this.name + " 觀察到 state 更改為:" + arg);
        }
    }
}
// 被觀察者,繼承 Observable 表示可以被觀察
public class ConcreteSubject extends Observable {
    private int state;

    public ConcreteSubject(int state) {
        this.setState(state);
    }

    public int getState() {
        return state;
    }

    public void setState(int state) {
        // 設(shè)置變化點
        super.setChanged();
        // 狀態(tài)變化,通知觀察者
        super.notifyObservers(state);
        this.state = state;
    }

    @Override
    public String toString() {
        return "state:" + this.state;
    }
}
public class TestObserve {
    public static void main(String[] args) {
        // 創(chuàng)建被觀察者
        ConcreteSubject subject = new ConcreteSubject(0);
        // 創(chuàng)建觀察者
        ConcreteObserver ConcreteObserverA = new ConcreteObserver("觀察者 A");
        ConcreteObserver ConcreteObserverB = new ConcreteObserver("觀察者 B");
        ConcreteObserver ConcreteObserverC = new ConcreteObserver("觀察者 C");
        // 添加可觀察對象
        subject.addObserver(ConcreteObserverA);
        subject.addObserver(ConcreteObserverB);
        subject.addObserver(ConcreteObserverC);

        System.out.println(subject);
        // Output:
        // state:0
        subject.setState(1);
        // Output:
        // 觀察者 C 觀察到 state 更改為:1
        // 觀察者 B 觀察到 state 更改為:1
        // 觀察者 A 觀察到 state 更改為:1
        System.out.println(subject);
        // Output:
        // state:1

    }
}

2.Guava對觀察者模式的支持

Guava 中使用 Event Bus 來實現(xiàn)對觀察者模式的支持。

com.google.common.eventbus.EventBus 提供了以下主要方法:

  • register(Object listener):將一個對象注冊為事件的監(jiān)聽器。
  • unregister(Object listener):從事件總線中注銷一個監(jiān)聽器。
  • post(Object event):發(fā)布一個事件到事件總線,以便通知所有注冊的監(jiān)聽器。
  • getSubscribers(Class<?> eventClass):返回訂閱指定事件類型的所有監(jiān)聽器的集合。

這些方法提供了事件的注冊、注銷、發(fā)布和獲取監(jiān)聽器等功能,使得開發(fā)者可以方便地使用 EventBus 進行事件驅(qū)動編程。

@Getter
@AllArgsConstructor
public class MyEvent {
    private String message;
}
@Slf4j
public class EventSubscriber {
    @Subscribe
    public void handleEvent(MyEvent event) {
        String message = event.getMessage();
        // Handle the event logic
        log.info("Received event: " + message);
    }
}
@Test
public void test() {
        EventBus eventBus = new EventBus();
        EventSubscriber subscriber = new EventSubscriber();
        eventBus.register(subscriber);

        // Publish an event
        eventBus.post(new MyEvent("Hello, World!"));
        // Output:
        // Received event: Hello, World!   
    }

3.Spring對觀察者模式的支持

Spring 中可以使用 Spring Event 來實現(xiàn)觀察者模式。

在Spring Event中,有一些核心的概念和組件,包括ApplicationEvent、ApplicationListener、ApplicationContext和ApplicationEventMulticaster。

(1)ApplicationEvent(應(yīng)用事件):

  • ApplicationEvent是Spring Event框架中的基礎(chǔ)類,它是所有事件類的父類。
  • 通過繼承ApplicationEvent,并定義自己的事件類,可以創(chuàng)建特定類型的事件對象。
  • 事件對象通常包含與事件相關(guān)的信息,例如狀態(tài)變化、操作完成等。

(2) ApplicationListener(應(yīng)用監(jiān)聽器):

  • ApplicationListener是Spring Event框架中的接口,用于監(jiān)聽并處理特定類型的事件。
  • 通過實現(xiàn)ApplicationListener接口,并指定感興趣的事件類型,可以創(chuàng)建具體的監(jiān)聽器。
  • 監(jiān)聽器可以定義在任何Spring Bean中,當(dāng)所監(jiān)聽的事件被發(fā)布時,監(jiān)聽器會自動接收到該事件,并執(zhí)行相應(yīng)的處理邏輯。

(3) ApplicationContext(應(yīng)用上下文):

  • ApplicationContext是Spring框架的核心容器,它負(fù)責(zé)管理Bean的生命周期和依賴關(guān)系。
  • 在Spring Event中,ApplicationContext是事件的發(fā)布者和訂閱者的容器。
  • 通過獲取ApplicationContext實例,可以獲取ApplicationEventPublisher來發(fā)布事件,也可以注冊ApplicationListener來監(jiān)聽事件。

(4) ApplicationEventMulticaster(事件廣播器):

  • ApplicationEventMulticaster是Spring Event框架中的組件,用于將事件廣播給各個監(jiān)聽器。
  • 它負(fù)責(zé)管理事件和監(jiān)聽器之間的關(guān)系,并將事件傳遞給對應(yīng)的監(jiān)聽器進行處理。
  • Spring框架提供了幾種實現(xiàn)ApplicationEventMulticaster的類,如SimpleApplicationEventMulticaster和AsyncApplicationEventMulticaster,用于支持不同的事件分發(fā)策略。

通過使用這些關(guān)鍵概念和組件,可以在 Spring 應(yīng)用程序中實現(xiàn)事件驅(qū)動的編程模型。事件發(fā)布者(ApplicationEventPublisher)可以發(fā)布特定類型的事件,而訂閱者(ApplicationListener)可以監(jiān)聽和處理已發(fā)布的事件。ApplicationContext作為容器,負(fù)責(zé)管理事件和監(jiān)聽器,并使用ApplicationEventMulticaster來實現(xiàn)事件的廣播和分發(fā)。

下面是使用 Spring Event 實現(xiàn)觀察者模式的例子:

/**
 * <p>
 * 基礎(chǔ)事件發(fā)布類
 * </p>
 *
 */

public abstract class BaseEvent<T> extends ApplicationEvent {

    /**
     * 該類型事件攜帶的信息
     */
    private T eventData;

    /**
     *
     * @param source 最初觸發(fā)該事件的對象
     * @param eventData 該類型事件攜帶的信息
     */
    public BaseEvent(Object source, T eventData) {
        super(source);
        this.eventData = eventData;
    }

    public T getEventData() {
        return eventData;
    }
}

這里定義了一個基礎(chǔ)事件發(fā)布抽象類,所有的事件發(fā)布類都可以繼承此類。

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public  class User {
    private Integer userId;
    private String userName;
}
public class UserEvent  extends BaseEvent<User>{
    private static final long serialVersionUID = 8145130999696021526L;

    public UserEvent(Object source, User user) {
        super(source,user);
    }

}
@Slf4j
@Service
public class UserListener {
    /*
     * @Async加了就是異步監(jiān)聽,沒加就是同步(啟動類要開啟@EnableAsync注解)
     * 可以使用@Order定義監(jiān)聽者順序,默認(rèn)是按代碼書寫順序
     * 如果返回類型不為void,則會被當(dāng)成一個新的事件,再次發(fā)布
     * @EventListener注解在EventListenerMethodProcessor類被掃描
     * 可以使用SpEL表達(dá)式來設(shè)置監(jiān)聽器生效的條件
     * 監(jiān)聽器可以看做普通方法,如果監(jiān)聽器拋出異常,在publishEvent里處理即可
     */

    //@Async
    @Order(1)
    @EventListener(condition = "#userEvent.getEventData().getUserName().equals('小明')")
    public String lister1(UserEvent userEvent){
        User user =userEvent.getEventData();
        log.info(user.toString());
        return "小米";
    }

    @Async
    @Order(2)
    @EventListener
    public void lister3(UserEvent userEvent){
        log.info("監(jiān)聽者2");
    }
    @Async
    @Order(3)
    @EventListener
    public void lister2(String name){
        log.info("我叫:"+name);
    }
    
}
@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class ObserveTest {
    @Resource
    private ApplicationEventPublisher applicationEventPublisher;
    @Test
    public void test() {
        applicationEventPublisher.publishEvent(new UserEvent(this, new User(1, "小明")));
        // Output:
        // User(userId=1, userName=小明)
        // 我叫:小米
        // 監(jiān)聽者2
    }

}

IDEA 中可以直接跳轉(zhuǎn)到對應(yīng)的監(jiān)聽器。

相比于 Guava Event Bus,Spring Event 在實現(xiàn)觀察者模式時具有以下優(yōu)點:

  • 集成性:Spring Event 是 Spring 框架的一部分,可以與其他 Spring 組件(如 Spring Boot、Spring MVC 等)無縫集成。這使得在一個應(yīng)用程序中使用 Spring Event 變得更加方便和統(tǒng)一。
  • 注解驅(qū)動:Spring Event 支持使用注解來聲明事件監(jiān)聽器和發(fā)布事件。通過使用 @EventListener 注解,開發(fā)人員可以輕松定義事件監(jiān)聽器方法,并且不需要顯式注冊和注銷監(jiān)聽器。

三、優(yōu)缺點

觀察者模式有以下幾個優(yōu)點:

  • 解耦性:觀察者模式能夠?qū)⒅黝}和觀察者之間的耦合度降到最低。主題與觀察者之間都是松散耦合的關(guān)系,它們之間可以獨立地進行擴展和修改,而不會相互影響。
  • 靈活性:通過使用觀察者模式,可以動態(tài)地添加、刪除和通知觀察者,使系統(tǒng)更加靈活。無需修改主題或觀察者的代碼,就可以實現(xiàn)新的觀察者加入和舊觀察者離開的功能。
  • 一對多關(guān)系:觀察者模式支持一對多的依賴關(guān)系,一個主題可以有多個觀察者。這樣可以方便地實現(xiàn)消息的傳遞和廣播,當(dāng)主題狀態(tài)更新時,所有觀察者都能得到通知。

雖然觀察者模式具有許多優(yōu)點,但也存在一些缺點:

  • 可能引起性能問題:如果觀察者較多或通知過于頻繁,可能會導(dǎo)致性能問題。每個觀察者都需要接收通知并執(zhí)行相應(yīng)操作,當(dāng)觀察者較多時,可能會增加處理時間和系統(tǒng)負(fù)載。
  • 可能引起循環(huán)依賴:由于觀察者之間可以相互注冊,如果設(shè)計不當(dāng),可能會導(dǎo)致循環(huán)依賴的問題。這樣會導(dǎo)致觸發(fā)通知的死循環(huán),造成系統(tǒng)崩潰或異常。
  • 順序不確定性:在觀察者模式中,觀察者的執(zhí)行順序是不確定的。如果觀察者之間有依賴關(guān)系,可能會產(chǎn)生意外的結(jié)果。

綜上所述,觀察者模式在許多場景下都非常有用,但在使用時需要注意性能問題、循環(huán)依賴和執(zhí)行順序等方面的考慮。

責(zé)任編輯:趙寧寧 來源: Java隨想錄
相關(guān)推薦

2020-10-26 08:45:39

觀察者模式

2024-02-19 13:11:38

門面模式系統(tǒng)

2024-02-26 11:52:38

代理模式設(shè)計

2024-01-29 12:22:07

設(shè)計模式策略模式

2021-07-08 11:28:43

觀察者模式設(shè)計

2013-11-26 17:09:57

Android設(shè)計模式

2024-02-04 12:04:17

2024-02-27 11:59:12

享元模式對象

2024-02-21 12:24:33

模板設(shè)計模式框架

2024-01-30 13:15:00

設(shè)計模式責(zé)任鏈

2024-02-23 12:11:53

裝飾器模式對象

2023-05-22 13:27:17

2022-01-29 22:12:35

前端模式觀察者

2024-02-20 12:09:32

模式工廠方法接口

2024-02-22 12:13:49

適配器模式代碼

2015-11-25 11:10:45

Javascript設(shè)計觀察

2021-09-06 10:04:47

觀察者模式應(yīng)用

2009-03-30 09:39:04

觀察者思想換位設(shè)計模式

2022-05-05 16:47:24

Docker網(wǎng)絡(luò)空間容器

2024-06-04 13:11:52

Python行為設(shè)計模式開發(fā)
點贊
收藏

51CTO技術(shù)棧公眾號