實例解析不雅察者形式及其在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)假如某些不雅察者的呼應辦法被壅塞,全部告訴進程即被壅塞,其它不雅察者不克不及實時被告訴