有個日本銀行項目,今天本人要做架構支援,很多不懂,所以初步研究了一下.感想如下.
Java深入到一定程度,就不可避免的碰到設計模式(design pattern)這一概念,瞭解設計模式,將使自己對java中的介面或抽象類別應用有更深的理解.設計模式在java的中型系統中應用廣泛,遵循一定的編程模式,才能使自己的代碼便於理解,易於交流,Observer(觀察者)模式是比較常用的一個模式,尤其在介面設計中應用廣泛.
例子以後再加,忙!!!!!!!!!
來了
在設計一組依賴的對象與它們所依賴的對象之間一致(同步)的交流模型時,觀察者模式(Observer Pattern)很有用。它可以使依賴對象的狀態與它們所依賴的對象的狀態保持同步。這組依賴的對象指的是觀察者(Observer),它們所依賴的對象稱為主題(Subject)。為了實現觀察者(Observer)的狀態與主題(Subject)保持同步,觀察者模式(Observer Pattern)
推薦採用發行者--訂閱者(publisher--subscriber)模型,以使這組觀察者(Observer)和主題(Subject)對象之間有清晰的界限。
典型的觀察者(Observer)是一個依賴於或者關注於主題對象的狀態的對象。一個主題可以有一個或者多個觀察者。這些觀察者在主體的狀態發生變化時,需要得到通知。
由於給定主體的觀察者鏈表需要動態變化,因此一個主題不能維護一個靜態觀察者鏈表。因此關注於主題狀態的任何對象都需要明確地註冊自己為主體的一個觀察者。主題狀態發生的變化,都需要通知所有的以註冊的觀察者。從主題接到通知以後,每一個觀察者查詢主題,使自己的狀態與主題的同步。因此一個主題扮演著發行者的角色,發布資訊到所有的以訂閱的觀察者。
換句話說,主題和它的觀察者之間包含了一對多的關係。當主題的執行個體的狀態發生變化時,所有的依賴於它的觀察者都會得到通知並更新自己。每一個觀察者對象需要向主題註冊,當主題的狀態發生變化的時候得到通知。一個觀察者可以註冊或者訂閱多個主題。當觀察者不希望再得到通知時,它可以向主題進行登出。
為了實現這種機制:
(1) 主題需要為註冊和登出通知提供一個介面。
(2) 下面的兩點也需要滿足:
A、 拉模型(In the pull model)--主題需要提供一個介面,可以使觀察者查詢主題獲得需要的狀態資訊來更新自己的狀態。
B、 推模型(In the push model)--主題發送觀察者可能關注的狀態資訊。
(3) 觀察者需要提供一個可以從主題接受通知的介面。
類圖(圖33.1)描述了為滿足於以上需求,不同類的結構和它們之間的關聯關係。
Figure 33.1: Generic Class Association When the Observer Pattern Is Applied
從這個類圖可以看到:
(1) 所有的主題需要提供一個類似於Observable介面的實現。
(2) 所有的觀察者需要提供一個類似於Observer介面的實現。
在應用觀察者模式時,有幾種變體。這就會產生不同類型的主題--觀察者模式,例如,觀察者僅關注主體特定類型的變化等。
增加新的觀察者:
應用觀察者模式以後,在不影響主題類的情況下,可以動態加入不同的觀察者。同樣,主題的狀態變化邏輯改變時,觀察者也不會受到影響。
例子:
為了管理一個賣廠多個分類產品,讓我們建立一個銷售報告系統。這個系統有以下特徵:
(1) 使用者可以選擇一個他們感興趣的分類
(2) 在選擇了一個分類以後,需要顯示下面的兩種類型的報表。
A、 月度報表(Monthly report)--所選分類當月的所有交易清單。
B、 年度累積額(YTD sales chart)--以月為單位顯示選擇分類的年度累積額圖。
(3) 當一個不同的分類被選擇時,兩種報表的資料會被重新整理,顯示當前所選分類的報表。
為了實現以上期望的功能,我們很容易的看到兩個報表對象依賴於持有使用者選擇分類的對象。應用觀察者模式於此情境,我們可以設計一個介於持有使用者選擇分類的對象和兩個報表對象之間一個一致(同步)的交流模型。
讓我們定義三個類,它們的功能如表33.1所描述:
Table 33.1: Subject-Observer Classes
public interface Observable {
public void notifyObservers();
public void register(Observer obs);
public void unRegister(Observer obs);
}
ReportManager類(Listing33.1)提供了聲明在Observable介面中方法的實現。兩個依賴於ReportManager的報表對象使用這些方法註冊它們自己為觀察者。ReportManager把這些註冊的觀察者儲存到observersList向量(vector)中。當前選擇的分類構成了ReportManager對象的狀態,它以執行個體變數的形式儲存在變數department中。當為department設定一個新的值時(也就是ReportManager對象的狀態改變),notifyObservers方法被調用。作為notifyObservers方法的一部分,ReportManager調用註冊為觀察者的refreshData(Observable)方法。
Listing 33.1: ReportManager Class
public class ReportManager extends JFrame
implements Observable {
…
…
private Vector observersList;
private String department;
public ReportManager() throws Exception {
…
…
observersList = new Vector();
…
…
}
public void register(Observer obs) {
//Add to the list of Observers
observersList.addElement(obs);
}
public void unRegister(Observer obs) {
//remove from the list of Observers
}
public void notifyObservers() {
//Send notify to all Observers
for (int i = 0; i < observersList.size(); i++) {
Observer observer =
(Observer) observersList.elementAt(i);
observer.refreshData(this);
}
}
public String getDepartment() {
return department;
}
public void setDepartment(String dept) {
department = dept;
}
class ButtonHandler implements ActionListener {
ReportManager subject;
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals(ReportManager.EXIT)) {
System.exit(1);
}
if (e.getActionCommand().equals(ReportManager.SET_OK)) {
String dept = (String)
cmbDepartmentList.getSelectedItem();
//change in state
subject.setDepartment(dept);
subject.notifyObservers();
}
}
public ButtonHandler() {
}
public ButtonHandler(ReportManager manager) {
subject = manager;
}
}
}//end of class
除了提供Observable介面方法的實現,ReportManager還顯示了必要的使用者介面,允許使用者選擇一個特定的、關注的分類。
讓我們定義介面Observer的兩個實現:MonthlyReport和YTDChart類
public interface Observer {
public void refreshData(Observable subject);
}
Figure 33.2: Observer Class Hierarchy
Listing 33.2: MonthlyReport Class as an Observer
public class MonthlyReport extends JFrame implements Observer {
…
…
private ReportManager objReportManager;
public MonthlyReport(ReportManager inp_objReportManager)
throws Exception {
super("Observer Pattern -- Example");
objReportManager = inp_objReportManager;
//Create controls
…
…
//Create Labels
…
…
objReportManager.register(this);
}
public void refreshData(Observable subject) {
if (subject == objReportManager) {
//get subject's state
String department = objReportManager.getDepartment();
lblTransactions.setText(
"Current Month Transactions - " +
department);
Vector trnList =
getCurrentMonthTransactions(department);
String content = "";
for (int i = 0; i < trnList.size(); i++) {
content = content +
trnList.elementAt(i).toString() + "/n";
}
taTransactions.setText(content);
}
}
private Vector getCurrentMonthTransactions(String department
) {
Vector v = new Vector();
FileUtil futil = new FileUtil();
Vector allRows = futil.fileToVector("Transactions.date");
//current month
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
int month = cal.get(Calendar.MONTH) + 1;
String searchStr = department + "," + month + ",";
int j = 1;
for (int i = 0; i < allRows.size(); i++) {
String str = (String) allRows.elementAt(i);
if (str.indexOf(searchStr) > ?1) {
StringTokenizer st =
new StringTokenizer(str, ",");
st.nextToken();//bypass the department
str = " " + j + ". " + st.nextToken() + "/" +
st.nextToken() + "~~~" +
st.nextToken() + "Items" + "~~~" +
st.nextToken() + " Dollars";
j++;
v.addElement(str);
}
}
return v;
}
}//end of class
ReportManager利用這個介面通知它的所有觀察者。
主題--觀察者的關聯(Subject--Observer Association)
通常,一個客戶首先需要建立一個主題(ReportManager)執行個體,當一個觀察者(例如:MonthlyReport,YTDChart)對象被建立。客戶把主題ReportManager執行個體的引用傳遞給觀察者的建構函式,觀察者將自身註冊到當前主題執行個體上。
//Client Code
public class SupervisorView {
…
…
public static void main(String[] args) throws Exception {
//Create the Subject
ReportManager objSubject = new ReportManager();
//Create Observers
new MonthlyReport(objSubject);
new YTDChart(objSubject);
}
}//end of class
類之間的關聯描述如下:
Figure 33.3: Example Application--Class Association
邏輯流程:
(1) 使用ReportManager使用者介面,當使用者選擇一個特定的分類並且點擊OK按鈕時,ReportManager的內部狀態被被改變(例如,ReportManager執行個體變數department的值發生改變)。
(2) 新的狀態一旦被設定,ReportManager調用兩個註冊的觀察者MonthlyReport和YTDChart的refreshData(Observable)方法。
(3) 作為refreshData方法的一部分,兩個report對象需要:
A、 檢查以確保調用refreshData方法的主題和觀察者這冊的主題是同一個主題。這就避免了觀察者響應不必要的調用。
B、 使用getDepartment方法查詢ReportManager的目前狀態。
C、 從資料檔案中提取響應的資料顯示。
Figure 33.4: MonthlyReport View
當ReportManager的狀態變化邏輯實現需要改變時,任何觀察者不受影響。同樣,當一個新的觀察者被加入時,ReportManager類不需要任何變化。