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

2020-12-16 圖靈學院

本文主要內容:

介紹觀察者模式微信公眾號的發布/訂閱示例觀察者模式總結分析觀察者模式的典型應用JDK 提供的觀察者接口中的觀察者模式Guava EventBus 中的觀察者模式JDK 委託事件模型DEM中的觀察者模式Spring ApplicationContext 事件機制中的觀察者模式觀察者模式

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

微信公眾號有服務號、訂閱號和企業號之分。以我的公眾號為例,我的公眾號類型是訂閱號,名稱是 「小旋鋒」,專注於大數據,Java後端類技術分享。目前主要是分享學習筆記為主,儘量做到 「原創」、」高質量」、」成體系」。每當我發布一篇博文推送,訂閱的用戶都能夠在我發布推送之後及時接收到推送,即可方便地在手機端進行閱讀。

微信公眾號.發布/訂閱

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

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

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

角色

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

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

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

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

示例

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

publicinterfaceSubscriber{intreceive(String publisher, String articleName);}然後是一個微信客戶端(具體觀察者),實現了 receive 方法

publicclassWeChatClientimplementsSubscriber{private String username;publicWeChatClient(String username){this.username = username; }@Overridepublicintreceive(String publisher, String articleName){// 接收到推送時的操作 System.out.println(String.format("用戶<%s> 接收到 <%s>微信公眾號 的推送,文章標題為 <%s>", username, publisher, articleName));return0; }}發布者類(目標,被觀察對象),該類維護了一個訂閱者列表,實現了訂閱、取消訂閱、通知所有訂閱者等功能

publicclassPublisher{private List<Subscriber> subscribers;privateboolean pubStatus = false;publicPublisher(){ subscribers = new ArrayList<Subscriber>(); }protectedvoidsubscribe(Subscriber subscriber){this.subscribers.add(subscriber); }protectedvoidunsubscribe(Subscriber subscriber){if (this.subscribers.contains(subscriber)) {this.subscribers.remove(subscriber); } }protectedvoidnotifySubscribers(String publisher, String articleName){if (this.pubStatus == false) {return; }for (Subscriber subscriber : this.subscribers) { subscriber.receive(publisher, articleName); }this.clearPubStatus(); }protectedvoidsetPubStatus(){this.pubStatus = true; }protectedvoidclearPubStatus(){this.pubStatus = false; }}微信公眾號類(具體目標),該類提供了 publishArticles 方法,用於發布推送,當文章發布完畢時調用父類的通知所有訂閱者方法

publicclassWeChatAccountsextendsPublisher{private String name;publicWeChatAccounts(String name){this.name = name; }publicvoidpublishArticles(String articleName, String content){ System.out.println(String.format("\n<%s>微信公眾號 發布了一篇推送,文章名稱為 <%s>,內容為 <%s> ", this.name, articleName, content)); setPubStatus(); notifySubscribers(this.name, articleName); }}測試

publicclassTest{publicstaticvoidmain(String[] args){ WeChatAccounts accounts = new WeChatAccounts("小旋鋒"); WeChatClient user1 = new WeChatClient("張三"); WeChatClient user2 = new WeChatClient("李四"); WeChatClient user3 = new WeChatClient("王五"); accounts.subscribe(user1); accounts.subscribe(user2); accounts.subscribe(user3); accounts.publishArticles("設計模式 | 觀察者模式及典型應用", "觀察者模式的內容..."); accounts.unsubscribe(user1); accounts.publishArticles("設計模式 | 單例模式及典型應用", "單例模式的內容...."); }}結果如下,符合預期,當公眾號發布一篇推送時,訂閱該公眾號的用戶可及時接收到推送的通知

<小旋鋒>微信公眾號 發布了一篇推送,文章名稱為 <設計模式 | 觀察者模式及典型應用>,內容為 <觀察者模式的內容...>用戶<張三> 接收到 <小旋鋒>微信公眾號 的推送,文章標題為 <設計模式 | 觀察者模式及典型應用>用戶<李四> 接收到 <小旋鋒>微信公眾號 的推送,文章標題為 <設計模式 | 觀察者模式及典型應用>用戶<王五> 接收到 <小旋鋒>微信公眾號 的推送,文章標題為 <設計模式 | 觀察者模式及典型應用><小旋鋒>微信公眾號 發布了一篇推送,文章名稱為 <設計模式 | 單例模式及典型應用>,內容為 <單例模式的內容....>用戶<李四> 接收到 <小旋鋒>微信公眾號 的推送,文章標題為 <設計模式 | 單例模式及典型應用>用戶<王五> 接收到 <小旋鋒>微信公眾號 的推送,文章標題為 <設計模式 | 單例模式及典型應用>可畫出類圖如下

示例.觀察者模式類圖

觀察者模式總結

觀察者模式的主要優點如下:

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

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

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

JDK 提供的觀察者接口

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

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

publicinterfaceObserver{voidupdate(Observable o, Object arg);}Observable 類則為目標類,相比我們的示例中的 Publisher 類多了並發和NPE方面的考慮

publicclassObservable{privateboolean changed = false;private Vector<Observer> obs = new Vector();publicObservable(){ }// 用於註冊新的觀察者對象到向量中publicsynchronizedvoidaddObserver(Observer var1){if (var1 == null) {thrownew NullPointerException(); } else {if (!this.obs.contains(var1)) {this.obs.addElement(var1); } } }// 用於刪除向量中的某一個觀察者對象publicsynchronizedvoiddeleteObserver(Observer var1){this.obs.removeElement(var1); }publicvoidnotifyObservers(){this.notifyObservers((Object)null); }// 通知方法,用於在方法內部循環調用向量中每一個觀察者的update()方法publicvoidnotifyObservers(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); } }// 用於清空向量,即刪除向量中所有觀察者對象publicsynchronizedvoiddeleteObservers(){this.obs.removeAllElements(); }// 該方法被調用後會設置一個boolean類型的內部標記變量changed的值為true,表示觀察目標對象的狀態發生了變化protectedsynchronizedvoidsetChanged(){this.changed = true; }// 用於將changed變量的值設為false,表示對象狀態不再發生改變或者已經通知了所有的觀察者對象,調用了它們的update()方法protectedsynchronizedvoidclearChanged(){this.changed = false; }// 返回對象狀態是否改變publicsynchronizedbooleanhasChanged(){returnthis.changed; }// 返回向量中觀察者的數量publicsynchronizedintcountObservers(){returnthis.obs.size(); }}我們可以使用 Observable 類以及 Observer 接口來重新實現微信公眾號示例。

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

@Data@AllArgsConstructorpublicclassWechatNotice{private String publisher;private String articleName;}然後改寫 WeChatClient 和 WeChatAccounts,分別實現JDK的 Observer 接口和繼承 Observable 類

publicclassWeChatClientimplementsObserver{private String username;publicWeChatClient(String username){this.username = username; }@Overridepublicvoidupdate(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())); }}publicclassWeChatAccountsextendsObservable{private String name;publicWeChatAccounts(String name){this.name = name; }publicvoidpublishArticles(String articleName, String content){ System.out.println(String.format("\n<%s>微信公眾號 發布了一篇推送,文章名稱為 <%s>,內容為 <%s> ", this.name, articleName, content)); setChanged(); notifyObservers(new WechatNotice(this.name, articleName)); }}測試,與示例中的測試代碼的區別在於調用的方法不同

publicclassTest{publicstaticvoidmain(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 提供了 register 和 unregister 方法用於註冊與取消註冊事件,當 EventBus 調用 post 方法時將把事件分發給註冊的對象

使用 Guava 重新實現示例

@Data@AllArgsConstructorpublicclassWechatNotice{private String publisher;private String articleName;}publicclassWeChatClient{private String username;publicWeChatClient(String username){this.username = username; }@Subscribepublicvoidlisten(WechatNotice notice){ System.out.println(String.format("用戶<%s> 接收到 <%s>微信公眾號 的推送,文章標題為 <%s>", username, notice.getPublisher(), notice.getArticleName())); }}publicclassWeChatAccounts{private String name;private EventBus eventBus;publicWeChatAccounts(String name){this.name = name;this.eventBus = new EventBus(); }publicvoidpublishArticles(String articleName, String content){ System.out.println(String.format("\n<%s>微信公眾號 發布了一篇推送,文章名稱為 <%s>,內容為 <%s> ", this.name, articleName, content));this.eventBus.post(new WechatNotice(this.name, articleName)); }publicvoidregister(WeChatClient weChatClient){this.eventBus.register(weChatClient); }publicvoidunregister(WeChatClient weChatClient){this.eventBus.unregister(weChatClient); }}測試

publicclassTest{publicstaticvoidmain(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("設計模式 | 單例模式及典型應用", "單例模式的內容...."); }}不出意料,輸出的內容與上面兩個示例一樣

<小旋鋒>微信公眾號 發布了一篇推送,文章名稱為 <設計模式 | 觀察者模式及典型應用>,內容為 <觀察者模式的內容...>用戶<張三> 接收到 <小旋鋒>微信公眾號 的推送,文章標題為 <設計模式 | 觀察者模式及典型應用>用戶<李四> 接收到 <小旋鋒>微信公眾號 的推送,文章標題為 <設計模式 | 觀察者模式及典型應用>用戶<王五> 接收到 <小旋鋒>微信公眾號 的推送,文章標題為 <設計模式 | 觀察者模式及典型應用><小旋鋒>微信公眾號 發布了一篇推送,文章名稱為 <設計模式 | 單例模式及典型應用>,內容為 <單例模式的內容....>用戶<李四> 接收到 <小旋鋒>微信公眾號 的推送,文章標題為 <設計模式 | 單例模式及典型應用>用戶<王五> 接收到 <小旋鋒>微信公眾號 的推送,文章標題為 <設計模式 | 單例模式及典型應用>JDK 委託事件模型DEM中的觀察者模式

首先來敲一個AWT按鈕監聽事件的Demo

import java.awt.*;import java.awt.event.MouseAdapter;import java.awt.event.MouseEvent;import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent;publicclassMouseEvents{private Frame frame;private Button button; MouseEvents() { frame = new Frame("點擊按鈕觸發點擊事件,控制臺將列印日誌"); frame.setBounds(300, 200, 600, 300); frame.setLayout(new FlowLayout()); button = new Button("this is a button"); button.setFont(new Font("Default", 0, 30)); frame.add(button); dealwithEvent(); frame.setVisible(true); }//事件監聽器以及處理事件privatevoiddealwithEvent(){// 監聽窗體關閉事件 frame.addWindowListener(new WindowAdapter() {@OverridepublicvoidwindowClosing(WindowEvent e){ System.exit(0); } }); button.addActionListener(new ActionListener() {privateint eventCount = 1;@OverridepublicvoidactionPerformed(ActionEvent e){ System.out.println(String.format("動作事件發生 %d 次", eventCount++)); } }); }publicstaticvoidmain(String[] args){new MouseEvents(); }}運行 main 方法桌面將彈出下面的面板和按鈕

按鈕監聽滑鼠事件

按鈕的 addActionListener 添加指定的動作偵聽器,以接收發自此按鈕的動作事件,當用戶在按鈕上按下或釋放滑鼠時,JVM將產生一個相應的 ActionEvent 類型的事件對象,並在觸發事件時將調用按鈕的 fireXXX() 方法(繼承自 Component),在該方法內部,將調用註冊到按鈕中的 ActionListener 對象的 actionPerformed() 方法(也就是我們實現的匿名事件處理類),實現對事件的處理

動作事件發生 1 次動作事件發生 2 次動作事件發生 3 次動作事件發生 4 次Spring ApplicationContext 事件機制中的觀察者模式

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

ApplicationContext:事件源,其中的 publishEvent()方法用於觸發容器事件ApplicationEvent:事件本身,自定義事件需要繼承該類,可以用來傳遞數據ApplicationListener:事件監聽器接口,事件的業務邏輯封裝在監聽器裡面使用 spring 事件機制重新實現示例

@DatapublicclassWechatNoticeextendsApplicationEvent{private String publisher;private String articleName;publicWechatNotice(Object source, String publisher, String articleName){super(source);this.publisher = publisher;this.articleName = articleName; }}publicclassWeChatClientimplementsApplicationListener{private String username;publicWeChatClient(String username){this.username = username; }@OverridepublicvoidonApplicationEvent(ApplicationEvent event){if (event instanceof WechatNotice) { WechatNotice notice = (WechatNotice) event; System.out.println(String.format("用戶<%s> 接收到 <%s>微信公眾號 的推送,文章標題為 <%s>", username, notice.getPublisher(), notice.getArticleName())); } }publicvoidsetUsername(String username){this.username = username; }}publicclassWeChatAccountsimplementsApplicationContextAware{private ApplicationContext ctx;private String name;publicWeChatAccounts(String name){this.name = name; }publicvoidsetName(String name){this.name = name; }@OverridepublicvoidsetApplicationContext(ApplicationContext applicationContext)throws BeansException {this.ctx = applicationContext; }publicvoidpublishArticles(String articleName, String content){ System.out.println(String.format("\n<%s>微信公眾號 發布了一篇推送,文章名稱為 <%s>,內容為 <%s> ", this.name, articleName, content)); ctx.publishEvent(new WechatNotice(this.name, this.name, articleName)); }}在 resources 目錄下創建 spring.xml 文件,填入下面的內容

<beansxmlns="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"><beanid="WeChatAccounts"class="com.observer.sprintevent.WeChatAccounts"scope="prototype"><constructor-argname="name"value=""></constructor-arg></bean><beanid="WeChatClient1"class="com.observer.sprintevent.WeChatClient"><constructor-argname="username"value="張三"></constructor-arg></bean><beanid="WeChatClient2"class="com.observer.sprintevent.WeChatClient"><constructor-argname="username"value="李四"></constructor-arg></bean><beanid="WeChatClient3"class="com.observer.sprintevent.WeChatClient"><constructor-argname="username"value="王五"></constructor-arg></bean></beans>測試

publicclassTest{publicstaticvoidmain(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;publicvoidpublishEvent(ApplicationEvent event){this.getApplicationEventMulticaster().multicastEvent(event);if (this.parent != null) {this.parent.publishEvent(event); }}ApplicationEventMulticaster getApplicationEventMulticaster()throws IllegalStateException {returnthis.applicationEventMulticaster;}protectedvoidinitApplicationEventMulticaster(){ 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 方法,與示例本質上是一致的

publicclassSimpleApplicationEventMulticasterextendsAbstractApplicationEventMulticaster{private Executor taskExecutor;publicvoidmulticastEvent(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() {publicvoidrun(){ listener.onApplicationEvent(event); } }); } else { listener.onApplicationEvent(event); } } }}

相關焦點

  • Golang語言情懷-第38期 Go 語言設計模式 監視器(觀察者)
    1 定義觀察者模式(Observer):定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個柱體對象,這個柱體對象在狀態發生變化時,會通知所有觀察者對象
  • 聽說你還不會寫觀察者模式?
    (這裡還沒有使用觀察者模式)為了簡單,我不使用接口,直接使用老闆,程式設計師,小美和客戶端四個類。以上的實現方式就是通過觀察者模式實現的,觀察者模式定義:定義對象間的一種一對多的依賴關係。上面老闆和程式設計師的關係就是典型的 一對多關係,並且程式設計師是否加班的狀態也是由老闆決定的,程式設計師會根據老闆的通知執行相對應的行為,而老闆不需要知道具體有哪些程式設計師,只需要維護一個程式設計師的列表就可以了,通知時遍歷這個列表。
  • 如何應用GOF設計模式中的創建型模式實現鬆耦合地創建對象實例
    軟體項目實訓及課程設計指導——如何應用GOF設計模式中的創建型模式實現鬆耦合地創建對象實例1、GOF設計模式中的工廠模式在GOF設計模式的劃分中,把與實現對象的實例化相關的模式稱之為「創建型模式」——而其中的工廠模式為創建型模式中的一種具體實現形式。
  • 設計模式——策略模式
    設計模式——策略模式1. 簡單工廠實現面向對象的編程,並不是類越多越好,類的劃分是為了封裝,但分類的基礎是抽象,具有相同屬性和功能的對象的抽象集合才是類。2.策略模式就是用來封裝算法的,但在實踐中,我們發現可以用它來封裝幾乎任何類型的規則,只要在分析過程中聽到需要在不同時間應用不同的業務規則,就可以考慮使用策略模式處理這種變化的可能性。
  • 追MM與設計模式(23種設計模式巧妙解析,趣味理解)
    9、DECORATOR—Mary過完輪到Sarly過生日,還是不要叫她自己挑了,不然這個月夥食費肯定玩完,拿出我去年在華山頂上照的照片,在背面寫上「最好的的禮物,就是愛你的Fita」,再到街上禮品店買了個像框(賣禮品的MM也很漂亮哦),再找隔壁搞美術設計的Mike設計了一個漂亮的盒子裝起來……,我們都是Decorator,最終都在修飾我這個人呀,怎麼樣,看懂了嗎?
  • 如何應用策略設計模式分離JDBC資料庫連接中的外部環境信息
    軟體項目實訓及課程設計指導——如何應用策略設計模式分離JDBC資料庫連接中的外部環境信息1、什麼是策略(Strategy)設計模式策略設計模式把「算法」(也就是軟體應用系統中的業務規則或者待實現的功能等)和「環境」(封裝軟體應用系統在實際應用時的場景)相互分離
  • 課程設計的模式
    課程設計需要考慮學科、學生、社會及科技發展等因素,以下為常見的課程設計模式:一、目標模式根據課程設計者對學習者行為變化的期望而確定的教育目標進行課程設計的思路。典型代表人物:泰勒該模式產生於博比特提出的「活動分析」課程設計方法,由泰勒完善,他把課程設計過程概括為確定教育目標、選擇學習經驗、組織學習經驗、評價學習結果這樣四個步驟或階段。其中,塔巴將其充實成診斷需要、形成具體的目標、選擇內容、組織內容、選擇學習經驗(活動)、組織學習經驗(活動)、評價、檢查平衡性和順序性八個階段。
  • 如何正確地應用Web MVC架構模式分離表示層和模型處理層耦合關係
    軟體項目實訓及課程設計指導——如何正確地應用Web MVC架構模式分離表示層和模型處理層耦合關係1、MVC體系架構設計模式是用來幫助系統設計人員控制「變化」的一種設計模式MVC體系架構設計模式是上世紀80年代在Smalltalk-80中出現的一種軟體設計模式
  • 這些經典的專業結構設計模式,你掌握了多少?
    本文中筆者將討論一些能夠輕鬆實現上述功能的設計模式。筆者將討論每種模式,如何在雲原生環境中使用以及該何時使用。其中一些模式並不是那麼新穎,但是在當前網際網路規模的雲世界中非常有用。斷路器設計分布式系統時應考慮故障問題。
  • 精讀《設計模式 - Decorator 裝飾器模式》
    Decorator(裝飾器模式)Decorator(裝飾器模式)屬於結構型模式,是一種拓展對象額外功能的設計模式,別名 wrapper。意圖:動態地給一個對象添加一些額外的職責。就增加功能來說,Decorator 模式相比生成子類更為靈活。
  • 現代網際網路和移動應用中優秀的用戶界面模式
    這句引語的爭議性來源於它的簡要,因此對於它的詮釋也具有一定的開放性。內容中對於複製和內在化,原創性和創新性,模仿和同化的區別沒有講明。然而,這不是懶惰的藉口。當然,應該鼓勵學習他人的作品,站在巨人的肩膀上設計自己的產品為特定的人群解決問題-為了那些你終端用戶。UI設計模式-和線框案例,你準備好了嗎?
  • 走向數字設計:商業模式創新
    那麼從這一講,我將結合設計行業的痛點、需求等情況,針對數字設計的五大層級逐層進行講解。 數字設計的商業模式創新,是實現數字設計的底層基礎。唯有順應數字經濟特點、創新商業模式才能給數字設計帶來更大發展空間。 那麼數字設計時代的商業模式創新是指什麼呢?
  • 生態環保型網箱養殖模式典型案例剖析
    生態環保型網箱養殖模式典型案例剖析姚丁香等網箱養殖具有不佔用耕地、不依賴淡水資源消耗的特點,是大水面資源特別是貧營養水體綜合利用的重要形式之一,成為許多區域近年脫貧攻堅、漁民持續增收的重要抓手。據《中國漁業統計年鑑》數據顯示,2017年,我國淡水網箱養殖面積達約7000萬m 2 。然而,至少有兩方面因素迫切要求網箱養殖生態化。
  • 軟體項目實訓及課程設計指導——如何在數據持久層中應用DAO模式
    軟體項目實訓及課程設計指導——如何在J2EE應用系統數據持久層中應用DAO模式1、為什麼要在軟體應用系統中提供數據持久層軟體應用系統中的數據持久層主要為整個軟體應用系統提供數據訪問功能服務,從而可以使軟體應用系統中的業務層組件的設計和開發人員能夠專注於系統業務邏輯的開發
  • 讓設計模式飛一會兒|①開篇獲獎感言
    從今天開始我將正式開啟有關設計模式的系列文章的寫作,和大家一同來聊聊設計模式這個老生常談的玩意。關於設計模式的文章,書籍,多如牛毛,隨便百度、Google一下都能給你搜出不計其數關於講解設計模式的文章來。那為什麼我還要花費如此大的精力和時間,來寫這麼個耳熟能詳的東西呢?
  • 拉動文化和旅遊消費的八大典型模式
    1.制度設計:小心假設、謹慎擴大、成型示範  文旅消費的政策設計,可以分為探索、試點、擴大試點、形成示範的四個階段。  從探索到成型:逐漸顯現的文旅消費的八大模式  根據對全國首批國家文化和旅遊消費示範城市拉動文旅消費的主要做法和模式的觀測、研究,總結提煉了以下八大典型模式。
  • APP深色模式設計探索(上)
    深色模式(Dark Mode)近年來成為了一個時髦的詞彙,蘋果在2019年的WWDC大會上為iOS13引入深色模式,Google也在2019年Android Q版本中正式推出了深色模式。發布會之後,各大APP紛紛在應用商店上架了支持深色模式的新版本。「暗黑」時代已經到來,APP對深色模式的適配設計成為迫在眉睫的需求。設計師如何快速為APP產品設計出體驗良好的深色模式?
  • GOF設計模式之橋接模式
    我們需要準備3*4=12支蠟筆,也就是說必須準備12個具體的蠟筆類只需要準備三種型號的毛筆,外加四個顏料盒,用3+4=7個類就可以實現12支蠟筆的功能橋接模式的定義:將抽象與實現分離,使它們可以獨立變化。
  • 教學設計模式的核心要素及特徵
    關鍵詞:教學設計;模式;共有特徵教學設計模式有效性和易用性之間的矛盾教學設計模式在實踐應用中存在以下矛盾:一方面,多數教師依靠對教學的樸素理解和認知來設計課程,基本屬於「經驗型」教學設計,甚至相當比例的教師把教學設計等同於
  • 暗色模式將如何改變設計的未來
    深色模式適合應用的場景  按照 Apple 以往的經驗,新設計的推出,為了提高市場的覆蓋程度,都會備有一套默認適配方案,如當年的3D Touch,PPI 3X自適應方案等。因此,隨著 iOS 13的推出,筆者認為默認也是自帶適配方案的。舊版應用無需擔心。至於新版APP的開發...額...請使用新版開發套件。