程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 實例解析不雅察者形式及其在Java設計形式開辟中的應用

實例解析不雅察者形式及其在Java設計形式開辟中的應用

編輯:關於JAVA

實例解析不雅察者形式及其在Java設計形式開辟中的應用。本站提示廣大學習愛好者:(實例解析不雅察者形式及其在Java設計形式開辟中的應用)文章只能為提供參考,不一定能成為您想要的結果。以下是實例解析不雅察者形式及其在Java設計形式開辟中的應用正文


1、不雅察者形式(Observer)的界說:

不雅察者形式又稱為定閱—宣布形式,在此形式中,一個目的對象治理一切相依於它的不雅察者對象,而且在它自己的狀況轉變時自動收回告訴。這平日透過呼喚各不雅察者所供給的辦法來完成。此種形式平日被用來事宜處置體系。

1、不雅察者形式的普通構造

起首看下不雅察者形式的類圖描寫:

不雅察者形式的腳色以下:

Subject(籠統主題接口):界說了主題類中對不雅察者列表的一系列操作, 包含增長,刪除, 告訴等。
Concrete Subject(詳細主題類):
Observer(籠統不雅察者接口):界說了不雅察者對主題類更新狀況接收操作。
ConcreteObserver(詳細不雅察者類):完成不雅察者接口更新主題類告訴等邏輯。
從這個類圖可以看出, 主題類中保護了一個完成不雅察者接口的類列表, 主題類經由過程這個列表來對不雅察者停止一系列的增刪改操作。不雅察者類也能夠自動挪用update辦法來懂得獲得主題類的狀況更新信息。

以上的類圖所描寫的只是根本的不雅察者形式的思惟, 有許多缺乏。好比作為不雅察者也能夠自動定閱某類主題等。上面的例子將停止一些修改, 以便實用詳細的營業邏輯。

2、不雅察者形式示例

我們構建一個不雅察者和主題類, 不雅察者可以自動定閱主題或許撤消主題。主題類同一被一個主題治理者所治理。上面給出類圖:

Subject:

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:主題類治理器

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:

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);
  }
}

測試:我們將主題類和不雅察者籠統成寫者和讀者

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();
  }
}

以上就是不雅察者形式的小示例。可以看出每一個主題類都要保護一個響應的不雅察者列表, 這裡可以依據詳細主題的籠統條理進一步籠統, 將這類集合放到一個籠統類中去完成, 來配合保護一個列表, 固然詳細操作要看現實的營業邏輯。

2、Servlet中的Listener

再說Servlet中的Listener之前, 先說說不雅察者形式的另外一種形狀——事宜驅動模子。與下面提到的不雅察者形式的主題腳色一樣, 事宜驅動模子包含事宜源, 詳細事宜, 監聽器, 詳細監聽器。
Servlet中的Listener就是典范的事宜驅動模子。
JDK中有一套事宜驅動的類, 包含一個同一的監聽器接口和一個同一的事宜源, 源碼以下:

/**
 * A tagging interface that all event listener interfaces must extend.
 * @since JDK1.1
 */
public interface EventListener {
}

這是一個標記接口, JDK劃定一切監聽器必需繼續這個接口。

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種兩類事宜的監聽器接口, 詳細以下圖:

詳細觸發情境以下表:

2、一個詳細的Listener觸發進程

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

起首一個Servlet中, HttpServletRequest挪用setAttrilbute辦法時, 現實上是挪用的org.apache.catalina.connector.request#setAttrilbute辦法。 我們看下它的源碼:

public void setAttribute(String name, Object value) {
    ...
    //下面的邏輯代碼已省略
    // 此處即告訴監聽者
    notifyAttributeAssigned(name, value, oldValue);
  }

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

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.attributeWordStrd(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可使法式加倍靈巧。

3、綜合示例
舉個例子,假如你看過TVB的警匪片,你就曉得臥底的任務方法。普通一個警員能夠有幾個臥底,潛入仇敵外部,刺探新聞,臥底完整靠他的引導的指導干活,引導說幾點行為,他必需依照這個時光去履行,假如行為時光轉變,他也要立馬轉變本身合營行為的時光。引導派兩個臥底去打入仇敵外部,那末引導相當於籠統主題,而督察警官張三這小我派了兩個臥底李四和萬王五,張三就相當於詳細主題,臥底相當於籠統不雅察者,這兩名臥底是李四和王五就是詳細不雅察者,派的這個舉措相當於不雅察者在主題的掛號。那末這個類圖以下:

應用javaAPI來完成,代碼描寫以下:

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); 
  } 
} 
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); 
  } 
 
 
} 

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); 
  } 
 
 
 
} 

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"); 
     
  } 
 
} 

測試運轉成果:

臥底B接到新聞,行為時光為:02:25
臥底A接到新聞,行為時光為:02:25
===========因為新聞敗事,行為時光提早=========
臥底B接到新聞,行為時光為:01:05
臥底A接到新聞,行為時光為:01:05

4、總結

不雅察者形式界說了對象之間一對多的關系, 當一個對象(被不雅察者)的狀況轉變時, 依附它的對象都邑收到告訴。可以運用到宣布——定閱, 變更——更新這類營業場景中。
不雅察者和被不雅察者之間用松耦合的方法, 被不雅察者不曉得不雅察者的細節, 只曉得不雅察者完成了接口。
事宜驅動模子加倍靈巧,但也是支付了體系的龐雜性作為價值的,由於我們要為每個事宜源定制一個監聽器和事宜,這會增長體系的累贅。

不雅察者形式的焦點是先分清腳色、定位好不雅察者和被不雅察者、他們是多對一的關系。完成的症結是要樹立不雅察者和被不雅察者之間的接洽、好比在被不雅察者類中有個聚集是用於寄存不雅察者的、當被檢測的器械產生轉變的時刻就要告訴一切不雅察者。在不雅察者的結構辦法中將被不雅察者傳入、同時將自己注冊到被不雅察者具有的不雅察者名單中、即observers這個list中。

1.不雅察者形式長處:
(1)籠統主題只依附於籠統不雅察者
(2)不雅察者形式支撐播送通訊
(3)不雅察者形式使信息發生層和呼應層分別

2.不雅察者形式缺陷:
(1)如一個主題被年夜量不雅察者注冊,則告訴一切不雅察者會消費較高價值
(2)假如某些不雅察者的呼應辦法被壅塞,全部告訴進程即被壅塞,其它不雅察者不克不及實時被告訴

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved