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

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

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務器之家 - 編程語言 - JAVA教程 - 實例解析觀察者模式及其在Java設計模式開發中的運用

實例解析觀察者模式及其在Java設計模式開發中的運用

2020-05-01 13:15pastqing JAVA教程

觀察者模式定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某一個主題對象,這個主題對象在狀態上發生變化時,會通知所有觀察者對象,使它們能夠自動更新自己.下面就以實例解析觀察者模式及其在Java設計模式開發中的運

一、觀察者模式(Observer)的定義:

觀察者模式又稱為訂閱—發布模式,在此模式中,一個目標對象管理所有相依于它的觀察者對象,并且在它本身的狀態改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實現。此種模式通常被用來事件處理系統。

1、觀察者模式的一般結構

首先看下觀察者模式的類圖描述:

實例解析觀察者模式及其在Java設計模式開發中的運用

觀察者模式的角色如下:

Subject(抽象主題接口):定義了主題類中對觀察者列表的一系列操作, 包括增加,刪除, 通知等。
Concrete Subject(具體主題類):
Observer(抽象觀察者接口):定義了觀察者對主題類更新狀態接受操作。
ConcreteObserver(具體觀察者類):實現觀察者接口更新主題類通知等邏輯。
從這個類圖可以看出, 主題類中維護了一個實現觀察者接口的類列表, 主題類通過這個列表來對觀察者進行一系列的增刪改操作。觀察者類也可以主動調用update方法來了解獲取主題類的狀態更新信息。

以上的類圖所描述的只是基本的觀察者模式的思想, 有很多不足。比如作為觀察者也可以主動訂閱某類主題等。下面的例子將進行一些改動, 以便適用具體的業務邏輯。

2、觀察者模式示例

我們構建一個觀察者和主題類, 觀察者可以主動訂閱主題或者取消主題。主題類統一被一個主題管理者所管理。下面給出類圖:

實例解析觀察者模式及其在Java設計模式開發中的運用

Subject:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public interface Subject {
  //注冊一個observer
  public void register(Observer observer);
  //移除一個observer
  public void remove(Observer observer);
  //通知所有觀察者
  public void notifyObservers();
  //獲取主題類要發布的消息
  public String getMessage();
}
ConcerteSubject:
public class MySubject implements Subject {
  private List<Observer> observers;
  private boolean changed;
  private String message;
  //對象鎖, 用于同步更新觀察者列表
  private final Object mutex = new Object();
  public MySubject() {
    observers = new ArrayList<Observer>();
    changed = false;
  }
  @Override
  public void register(Observer observer) {
    if (observer == null)
      throw new NullPointerException();
      //保證不重復
    if (!observers.contains(observer))
      observers.add(observer);
  }
  @Override
  public void remove(Observer observer) {
    observers.remove(observer);
  }
  @Override
  public void notifyObservers() {
    // temp list
    List<Observer> tempObservers = null;
    synchronized (mutex) {
      if (!changed)
        return;
      tempObservers = new ArrayList<>(this.observers);
      this.changed = false;
    }
    for(Observer obj : tempObservers) {
      obj.update();
    }
  }
  //主題類發布新消息
  public void makeChanged(String message) {
    System.out.println("The Subject make a change: " + message);
    this.message = message;
    this.changed = true;
    notifyObservers();
  }
  @Override
  public String getMessage() {
    return this.message;
  }
}

ConcerteSubject做出更新時, 就通知列表中的所有觀察者, 并且調用觀察者update方法以實現接受通知后的邏輯。這里注意notifyObservers中的同步塊。在多線程的情況下, 為了避免主題類發布通知時, 其他線程對觀察者列表的增刪操作, 同步塊中用一個臨時List來獲取當前的觀察者列表。

SubjectManagement:主題類管理器

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class SubjectManagement {
  //一個記錄 名字——主題類 的Map
  private Map<String, Subject> subjectList = new HashMap<String, Subject>();
  public void addSubject(String name, Subject subject) {
    subjectList.put(name, subject);
  }
  public void addSubject(Subject subject) {
    subjectList.put(subject.getClass().getName(), subject);
  }
  public Subject getSubject(String subjectName) {
    return subjectList.get(subjectName);
  }
  public void removeSubject(String name, Subject subject) {
  }
  public void removeSubject(Subject subject) {
  }
  //singleton
  private SubjectManagement() {}
  public static SubjectManagement getInstance() {
    return SubjectManagementInstance.instance;
  }
  private static class SubjectManagementInstance {
    static final SubjectManagement instance = new SubjectManagement();
  }
}

主題類管理器的作用就是在觀察者訂閱某個主題時, 獲取此主題的實例對象。

Observer:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public interface Observer {
  public void update();
  public void setSubject(Subject subject);
}
ConcerteObserver:
public class MyObserver implements Observer {
  private Subject subject;
  // get the notify message from Concentrate Subject
  @Override
  public void update() {
    String message = subject.getMessage();
    System.out.println("From Subject " + subject.getClass().getName()
        + " message: " + message);
  }
  @Override
  public void setSubject(Subject subject) {
    this.subject = subject;
  }
  // subcirbe some Subject
  public void subscribe(String subjectName) {
    SubjectManagement.getInstance().getSubject(subjectName).register(this);
  }
  // cancel subcribe
  public void cancelSubcribe(String subjectName) {
    SubjectManagement.getInstance().getSubject(subjectName).remove(this);
  }
}

測試:我們將主題類和觀察者抽象成寫者和讀者

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ObserverTest {
  private static MySubject writer;
  @BeforeClass
  public static void setUpBeforeClass() throws Exception {
    writer = new MySubject();
    //添加一個名為Linus的作家
    SubjectManagement.getInstance().addSubject("Linus",writer);
  }
  @Test
  public void test() {
    //定義幾個讀者
    MyObserver reader1 = new MyObserver();
    MyObserver reader2 = new MyObserver();
    MyObserver reader3 = new MyObserver();
    reader1.setSubject(writer);
    reader2.setSubject(writer);
    reader3.setSubject(writer);
    reader1.subscribe("Linus");
    reader2.subscribe("Linus");
    reader3.subscribe("Linus");
    writer.makeChanged("I have a new Changed");
    reader1.update();
  }
}

以上就是觀察者模式的小示例。可以看出每個主題類都要維護一個相應的觀察者列表, 這里可以根據具體主題的抽象層次進一步抽象, 將這種聚集放到一個抽象類中去實現, 來共同維護一個列表, 當然具體操作要看實際的業務邏輯。

二、Servlet中的Listener

再說Servlet中的Listener之前, 先說說觀察者模式的另一種形態——事件驅動模型。與上面提到的觀察者模式的主題角色一樣, 事件驅動模型包括事件源, 具體事件, 監聽器, 具體監聽器。
Servlet中的Listener就是典型的事件驅動模型。
JDK中有一套事件驅動的類, 包括一個統一的監聽器接口和一個統一的事件源, 源碼如下:

?
1
2
3
4
5
6
/**
 * A tagging interface that all event listener interfaces must extend.
 * @since JDK1.1
 */
public interface EventListener {
}

這是一個標志接口, JDK規定所有監聽器必須繼承這個接口。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class EventObject implements java.io.Serializable {
  private static final long serialVersionUID = 5516075349620653480L;
  /**
   * The object on which the Event initially occurred.
   */
  protected transient Object source;
  /**
   * Constructs a prototypical Event.
   *
   * @param  source  The object on which the Event initially occurred.
   * @exception IllegalArgumentException if source is null.
   */
  public EventObject(Object source) {
    if (source == null)
      throw new IllegalArgumentException("null source");
    this.source = source;
  }
  /**
   * The object on which the Event initially occurred.
   *
   * @return  The object on which the Event initially occurred.
   */
  public Object getSource() {
    return source;
  }
  /**
   * Returns a String representation of this EventObject.
   *
   * @return A a String representation of this EventObject.
   */
  public String toString() {
    return getClass().getName() + "[source=" + source + "]";
  }
}

EvenObject是JDK給我們規定的一個統一的事件源。EvenObject類中定義了一個事件源以及獲取事件源的get方法。

下面就分析一下Servlet Listener的運行流程。

1、Servlet Listener的組成

目前, Servlet中存在6種兩類事件的監聽器接口, 具體如下圖:

實例解析觀察者模式及其在Java設計模式開發中的運用

具體觸發情境如下表:

實例解析觀察者模式及其在Java設計模式開發中的運用

2、一個具體的Listener觸發過程

我們以ServletRequestAttributeListener為例, 來分析一下此處事件驅動的流程。

首先一個Servlet中, HttpServletRequest調用setAttrilbute方法時, 實際上是調用的org.apache.catalina.connector.request#setAttrilbute方法。 我們看下它的源碼:

?
1
2
3
4
5
6
public void setAttribute(String name, Object value) {
    ...
    //上面的邏輯代碼已省略
    // 此處即通知監聽者
    notifyAttributeAssigned(name, value, oldValue);
  }

下面是notifyAttributeAssigned(String name, Object value, Object oldValue)的源碼

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
private void notifyAttributeAssigned(String name, Object value,
      Object oldValue) {
    //從容器中獲取webAPP中定義的Listener的實例對象
    Object listeners[] = context.getApplicationEventListeners();
    if ((listeners == null) || (listeners.length == 0)) {
      return;
    }
    boolean replaced = (oldValue != null);
    //創建相關事件對象
    ServletRequestAttributeEvent event = null;
    if (replaced) {
      event = new ServletRequestAttributeEvent(
          context.getServletContext(), getRequest(), name, oldValue);
    } else {
      event = new ServletRequestAttributeEvent(
          context.getServletContext(), getRequest(), name, value);
    }
    //遍歷所有監聽器列表, 找到對應事件的監聽器
    for (int i = 0; i < listeners.length; i++) {
      if (!(listeners[i] instanceof ServletRequestAttributeListener)) {
        continue;
      }
      //調用監聽器的方法, 實現監聽操作
      ServletRequestAttributeListener listener =
        (ServletRequestAttributeListener) listeners[i];
      try {
        if (replaced) {
          listener.attributeReplaced(event);
        } else {
          listener.attributeAdded(event);
        }
      } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        context.getLogger().error(sm.getString("coyoteRequest.attributeEvent"), t);
        // Error valve will pick this exception up and display it to user
        attributes.put(RequestDispatcher.ERROR_EXCEPTION, t);
      }
    }
  }

上面的例子很清楚的看出ServletRequestAttributeListener是如何調用的。用戶只需要實現監聽器接口就行。Servlet中的Listener幾乎涵蓋了Servlet整個生命周期中你感興趣的事件, 靈活運用這些Listenser可以使程序更加靈活。

三、綜合示例
舉個例子,如果你看過TVB的警匪片,你就知道臥底的工作方式。一般一個警察可能有幾個臥底,潛入敵人內部,打探消息,臥底完全靠他的領導的指示干活,領導說幾點行動,他必須按照這個時間去執行,如果行動時間改變,他也要立馬改變自己配合行動的時間。領導派兩個臥底去打入敵人內部,那么領導相當于抽象主題,而督察警官張三這個人派了兩個臥底李四和萬王五,張三就相當于具體主題,臥底相當于抽象觀察者,這兩名臥底是李四和王五就是具體觀察者,派的這個動作相當于觀察者在主題的登記。那么這個類圖如下:

實例解析觀察者模式及其在Java設計模式開發中的運用

利用javaAPI來實現,代碼描述如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package observer;
 
import java.util.List;
import java.util.Observable;
import java.util.Observer;
/**
 *描述:警察張三
 */
public class Police extends Observable {
 
  private String time ;
  public Police(List<Observer> list) {
    super();
    for (Observer o:list) {
      addObserver(o);
    }
  }
  public void change(String time){
    this.time = time;
    setChanged();
    notifyObservers(this.time);
  }
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package observer;
 
import java.util.Observable;
import java.util.Observer;
/**
 *描述:臥底A
 */
public class UndercoverA implements Observer {
 
  private String time;
  @Override
  public void update(Observable o, Object arg) {
    time = (String) arg;
    System.out.println("臥底A接到消息,行動時間為:"+time);
  }
 
 
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package observer;
 
import java.util.Observable;
import java.util.Observer;
/**
 *描述:臥底B
 */
public class UndercoverB implements Observer {
  private String time;
  @Override
  public void update(Observable o, Object arg) {
    time = (String) arg;
    System.out.println("臥底B接到消息,行動時間為:"+time);
  }
 
 
 
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package observer;
 
import java.util.ArrayList;
import java.util.List;
import java.util.Observer;
/**
 *描述:測試
 */
public class Client {
 
  /**
   * @param args
   */
  public static void main(String[] args) {
    UndercoverA o1 = new UndercoverA();
    UndercoverB o2 = new UndercoverB();
    List<Observer> list = new ArrayList<>();
    list.add(o1);
    list.add(o2);
    Police subject = new Police(list);
    subject.change("02:25");
    System.out.println("===========由于消息敗露,行動時間提前=========");
    subject.change("01:05");
     
  }
 
}

測試運行結果:

?
1
2
3
4
5
臥底B接到消息,行動時間為:02:25
臥底A接到消息,行動時間為:02:25
===========由于消息敗露,行動時間提前=========
臥底B接到消息,行動時間為:01:05
臥底A接到消息,行動時間為:01:05

四、總結

觀察者模式定義了對象之間一對多的關系, 當一個對象(被觀察者)的狀態改變時, 依賴它的對象都會收到通知。可以應用到發布——訂閱, 變化——更新這種業務場景中。
觀察者和被觀察者之間用松耦合的方式, 被觀察者不知道觀察者的細節, 只知道觀察者實現了接口。
事件驅動模型更加靈活,但也是付出了系統的復雜性作為代價的,因為我們要為每一個事件源定制一個監聽器以及事件,這會增加系統的負擔。

觀察者模式的核心是先分清角色、定位好觀察者和被觀察者、他們是多對一的關系。實現的關鍵是要建立觀察者和被觀察者之間的聯系、比如在被觀察者類中有個集合是用于存放觀察者的、當被檢測的東西發生改變的時候就要通知所有觀察者。在觀察者的構造方法中將被觀察者傳入、同時將本身注冊到被觀察者擁有的觀察者名單中、即observers這個list中。

1.觀察者模式優點:
(1)抽象主題只依賴于抽象觀察者
(2)觀察者模式支持廣播通信
(3)觀察者模式使信息產生層和響應層分離

2.觀察者模式缺點:
(1)如一個主題被大量觀察者注冊,則通知所有觀察者會花費較高代價
(2)如果某些觀察者的響應方法被阻塞,整個通知過程即被阻塞,其它觀察者不能及時被通知

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产一区二区免费 | 日本精品一区二 | 国产精品久久精品 | 黄色免费电影网站 | 国产96在线视频 | 亚洲国产aⅴ精品一区二区 少妇一级片免费看 | 成人免费乱码大片a毛片软件 | 欧美精品久久 | 欧美在线一区二区 | 国产区一区 | 一区二区三区欧美 | 亚洲天堂一区 | 91精品国产综合久久福利软件 | 综合激情网 | 久久久婷婷一区二区三区不卡 | 精品国产一二三区 | 欧美一二三 | 成人精品三级av在线看 | 国产精品一区二区不卡 | 中文字幕在线一区二区三区 | 99视频在线 | 色婷婷综合久久久中字幕精品久久 | 亚洲免费国产 | 欧美日韩中文字幕在线 | 久久成人免费视频 | 激情婷婷 | 欧洲亚洲精品久久久久 | 午夜天堂 | 中文字幕超清在线免费 | 色接久久| 亚洲精品视频免费 | 久久久99久久久国产自输拍 | 视频一区中文字幕 | 亚洲免费视频一区二区 | 色性视频 | 在线成人免费电影 | 国产精品久久久久无码av | 久久国产一区 | 精品国产鲁一鲁一区二区在线观看 | 日韩欧美在线观看 | 美女久久久 |