1 基礎知識
1.1 標準定義
觀察者(Observer)模式標準定義:定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時, 所有依賴於它的對象都得到通知並被自動更新。
1.2 分析和說明
觀察者(Observer)模式屬於對象行為型模式。觀察者模式定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態上發生變化時,會通知所有觀察者對象,使他們能夠自動更新自己。這一模式中主要針對兩個對象object和Observer。一個object可以有多個Observer,當—個object對象的狀態發生改變時,所有依賴於它的Observer對象都得到通知被自動更新。
觀察者(Observer)模式結構如圖1所示,其角色包括抽象主題(Subject)角色、抽象觀察者(Observer)角色、具體主題(Concrete Subject)角色和具體觀察者(Concrete Observer)角色。
圖1 觀察者模式結構
抽象主題(Subject)角色:主題角色把所有對觀察對象的引用保存在此聚集裡,每個主題都可以有任何數量的觀察者。抽象主題提供一個接口,可以增加和刪除觀察者對象,主題角色又叫做抽象被觀察者(Observable)角色,一般用一個抽象類或者一個接口實現。
抽象觀察者(Observer)角色:為所有具體觀察者定義一個接口,在得到主題的通知時更新自己。這個接口叫做更新接口。抽象觀察者角色一般用一個抽象類或者一個接口實現,在這個示意性的實現中,更新接口只包含一個方法(即update()方法),這個方法叫做更新方法。
具體主題(Concrete Subject)角色:將有關狀態存主具體觀察者對象;在具體主題的內部狀態改變時,給所有登記過的觀察者發出通知。具體主題角色又叫做具體被觀察者角色(Concrete Observable)。具體主題角色負責實現對觀察者引用的聚集的管理方法。
具體觀察者(Concrete Observer)角色:存儲與主題的狀態自恰的狀態。具體觀察者角色實現抽象觀察者角色所要求的更新接口,以便使本身的狀態與主題的狀態相協調。如果需要,具體觀察者角色可以保存一個指向具體主題對象的引用。具體觀察者角色通常用一個具體子類實現。
2 應用場景舉例
比如公司的通訊錄是大家都要使用的,同時也是經常變化的。每次通訊錄的變化,都要把這些更新後的通訊錄發布給所有的公司員工。這就可以採用觀察者模式。用例見圖2所示。
圖2 觀察者模式用例圖
在這裡可以把AbstractAddressBook抽象類理解為抽象主題(Subject)角色。AbstractEmployee抽象類是抽象觀察者(Observer)角色。CompanyAddressBook類是具體主題(Concrete Subject)角色。CompanyEmployee類是具體觀察者(Concrete Observer)角色。其結構類圖如圖3所示。CompanyAddressBook類繼承AbstractAddressBook抽象類,CompanyEmployee類繼承AbstractEmployee抽象類。AbstractEmployee抽象類關聯AbstractAddressBook抽象類,即AbstractEmployee類有AbstractAddressBook類的屬性。
圖3 觀察者模式類圖
觀察者模式實現順序圖見圖4,實現順序描述:①基於CompanyAddressBook類創建一個addressBook對象;②基於CompanyEmployee類創建一個employee1對象;③基於CompanyEmployee類創建一個employee2對象;④調用addressBook對象的addEmployee方法,把employee1對象作為一個觀察者;⑤調用addressBook對象的addEmployee方法,把employee2對象作為一個觀察者;⑥ addressBook對象發生變化,通知employee1對象和employee2對象這兩個觀察者。
圖4 觀察者模式實現順序圖
3.Java的實現程序代碼
Java程序實現主要包括AbstractAddressBook抽象類文件,AbstractEmployee抽象類文件,CompanyAddressBook類文件和CompanyEmployee類文件等4個文件。其關係如圖3所示。下面分別列出這4個文件的程序代碼,最後列出測試代碼並顯示輸出結果。
AbstractAddressBook抽象類程序代碼清單01所示。
程序代碼清單01
public abstract class AbstractAddressBook {
protected List<AbstractEmployee> employeeList = new ArrayList<AbstractEmployee>();
protected String addressBook;
public String getAddressBook() {return addressBook;}
public void setAddressBook(String addressBook) {this.addressBook = addressBook; }
public void addEmployee(AbstractEmployee employee){employeeList.add(employee);}
public void removeEmployee(AbstractEmployee employee){employeeList.remove(employee);}
public void notice(){}
}
AbstractEmployee抽象類程序代碼清單02所示。
程序代碼清單02
public abstract class AbstractEmployee {
protected AbstractAddressBook addressBook ;
protected String addressBookContents;
public AbstractAddressBook getAddressBook() {return addressBook;}
public void setAddressBook(AbstractAddressBook addressBook) {this.addressBook = addressBook;}
public void update( AbstractAddressBook book ){
setAddressBook(book);
addressBookContents = addressBook.getAddressBook();
}
}
CompanyAddressBook繼承AbstractAddressBook抽象類,其程序代碼清單03所示。
程序代碼清單03
public class CompanyAddressBook extends AbstractAddressBook {
public CompanyAddressBook (){
addressBook = "舊的通信錄";
}
public void addEmployee(AbstractEmployee employee) {
//employee.setAddressBook(this);
employee.update(this);
super.addEmployee(employee);
}
public void notice() {
for (int i = 0; i < employeeList.size(); i++) {
employeeList.get(i).update(this);
}
}
}
CompanyEmployee繼承AbstractEmployee抽象類,其程序代碼清單04所示。
程序代碼清單04
public class CompanyEmployee extends AbstractEmployee{
private String employeeName ;
public String getEmployeeName() {return employeeName; }
public void setEmployeeName(String employeeName) {this.employeeName = employeeName;}
public CompanyEmployee(String employeeName) {
super();
this.employeeName = employeeName;
}
public void update( AbstractAddressBook book ){
super.update(book);
System.out.println(getEmployeeName()+"更新通訊錄。"+"通訊錄內容:"+addressBookContents);
}
}
觀察者模式測試程序的代碼清單05如下:
程序代碼清單05
public class Client {
public static void main(String[] args) {
System.out.println("——————原有的通訊錄——————");
AbstractAddressBook addressBook = new CompanyAddressBook();
AbstractEmployee employee1 = new CompanyEmployee("employee1");
AbstractEmployee employee2 = new CompanyEmployee("employee2");
addressBook.addEmployee(employee1);
addressBook.addEmployee(employee2);
System.out.println("——————更新的通訊錄——————");
String newAddressBook = "新的通訊錄";
addressBook.setAddressBook(newAddressBook);
addressBook.notice();
}
}
觀察者模式測試類輸出結果如下所示:
——————原有的通訊錄——————
employee1更新通訊錄。通訊錄內容:舊的通信錄
employee2更新通訊錄。通訊錄內容:舊的通信錄
——————更新的通訊錄——————
employee1更新通訊錄。通訊錄內容:新的通訊錄
employee2更新通訊錄。通訊錄內容:新的通訊錄
4.C#的實現程序代碼
C#與java程序包括一樣的接口和類。文件程序代碼清單06所示。
程序代碼清單06
using System;
using System.Collections.Generic;
using System.Text;
namespace Observer_DesignPattern{
public abstract class AbstractAddressBook {
protected List<AbstractEmployee> employeeList = new List<AbstractEmployee>();
protected String addressBook;
public String GetAddressBook(){ return addressBook;}
public void SetAddressBook(String addressBook){this.addressBook = addressBook;}
virtual public void AddEmployee(AbstractEmployee employee){
employeeList.Add(employee);
}
virtual public void RemoveEmployee(AbstractEmployee employee){
employeeList.Remove(employee);
}
abstract public void Notice();
}
public abstract class AbstractEmployee{
protected AbstractAddressBook addressBook;
protected String addressBookContents;
public AbstractEmployee(String employeeName) { }
public AbstractAddressBook GetAddressBook(){return addressBook;}
public void SetAddressBook(AbstractAddressBook addressBook){
this.addressBook = addressBook;
}
virtual public void Update(AbstractAddressBook book){
SetAddressBook(book);
addressBookContents = addressBook.GetAddressBook();
}
}
public class CompanyAddressBook : AbstractAddressBook{
public CompanyAddressBook(){addressBook = "舊的通信錄";}
override public void AddEmployee(AbstractEmployee employee){
employee.Update(this);
base.AddEmployee(employee);
}
override public void Notice(){
for (int i = 0; i < employeeList.Count; i++){ employeeList[i].Update(this); }
}
}
public class CompanyEmployee : AbstractEmployee{
private String employeeName;
public String EmployeeName{
get { return employeeName; }
set { employeeName = value; }
}
public CompanyEmployee(String employeeName): base(employeeName){
this.employeeName = employeeName;
}
override public void Update(AbstractAddressBook book){
base.Update(book);
Console.WriteLine(EmployeeName + "更新通訊錄。" + "通訊錄內容:" + addressBookContents);
}
}
}
觀察者模式測試程序的代碼清單07如下:
程序代碼清單07
class Observer_DesignPattern{
static void Main(string[] args) {
Console.WriteLine("——————原有的通訊錄——————");
AbstractAddressBook addressBook = new CompanyAddressBook();
AbstractEmployee employee1 = new CompanyEmployee("employee1");
AbstractEmployee employee2 = new CompanyEmployee("employee2");
addressBook.AddEmployee(employee1);
addressBook.AddEmployee(employee2);
Console.WriteLine("——————更新的通訊錄——————");
String newAddressBook = "新的通訊錄";
addressBook.SetAddressBook(newAddressBook);
addressBook.Notice();
}
}
觀察者模式測試類輸出結果如下所示:
——————原有的通訊錄——————
employee1更新通訊錄。通訊錄內容:舊的通信錄
employee2更新通訊錄。通訊錄內容:舊的通信錄
——————更新的通訊錄——————
employee1更新通訊錄。通訊錄內容:新的通訊錄
employee2更新通訊錄。通訊錄內容:新的通訊錄
5.C++的程序實現代碼
由於C++語言稍微不同於java和C#語言。需要首先定義頭文件,然後再是程序文件,同時,由於本程序調用了MFC,所以我們在頭文件中要引用MFC,如#include "stdafx.h"等。在定義過程中,VC並不是完全的面向對象的語言,故採用一些C++類來定義接口,主要區別是這些C++類的方法都是虛擬方法。這是C++語言與java和C#語言相差比較大的地方。
C++程序實現主要包括AddressBook抽象類文件,Employee抽象類文件,CompanyAddressBook類和CompanyEmployee類等4個文件。其關係如圖3所示。下面分別列出這4個文件的程序代碼,最後列出測試代碼並顯示輸出結果。
AddressBook抽象類文件和CompanyAddressBook類的頭文件代碼清單08所示。
程序代碼清單08
class Employee;
class AddressBook {
public:
CString getAddressBook();
void setAddressBook(CString addressBook);
void addEmployee(Employee* employee);
void removeEmployee(Employee* employee);
virtual void notice() = 0 ;
protected:
CList<Employee*, Employee*> EmployeeList;
CString addressBook;
};
class CompanyAddressBook : public AddressBook { //定義觀察者類
public:
CompanyAddressBook ();
void addEmployee(Employee* employee) ;
void notice() ;
};
AddressBook抽象類文件和CompanyAddressBook類的程序實現代碼清單09所示。
程序代碼清單09
CString AddressBook::getAddressBook(){return addressBook;}
void AddressBook::setAddressBook(CString book){addressBook = book;}
void AddressBook::addEmployee(Employee* employee){EmployeeList.AddTail(employee); }
void AddressBook::removeEmployee(Employee* employee){
POSITION pos = EmployeeList.Find(employee);
EmployeeList.RemoveAt(pos);
}
void AddressBook::notice(){}
CompanyAddressBook::CompanyAddressBook (){addressBook = "舊的通信錄";}
void CompanyAddressBook::addEmployee(Employee* employee) {
employee->update(this);
AddressBook::addEmployee(employee);
}
void CompanyAddressBook::notice(){
POSITION pos;
for( pos = EmployeeList.GetHeadPosition();pos;EmployeeList.GetNext(pos) ) {
EmployeeList.GetAt(pos)->update(this);
}
}
Employee抽象類文件和CompanyEmployee類的頭文件代碼清單10所示。
程序代碼清單10
class Employee { //定義Employee祖先類
public:
AddressBook* getAddressBook() ;
void setAddressBook(AddressBook* addressBook) ;
virtual void update(AddressBook* book) = 0;
protected:
AddressBook* addressBook ;
CString addressBookContents ;
};
class CompanyEmployee : public Employee { //定義CompanyEmployee實現類
public:
CompanyEmployee(CString name) ;
CString getEmployeeName() ;
void setEmployeeName(CString name) ;
void update(AddressBook* book);
private:
CString employeeName ;
};
Employee抽象類文件和CompanyEmployee類的程序實現代碼清單11所示。
程序代碼清單11
AddressBook* Employee::getAddressBook() {return addressBook;}
void Employee::setAddressBook(AddressBook* book) {addressBook = book;}
void Employee::update(AddressBook* book){
setAddressBook(book);
addressBookContents = addressBook->getAddressBook();
//printf("Employee更新通訊錄。 \n");
}
CompanyEmployee::CompanyEmployee(CString name){employeeName = name ;}
CString CompanyEmployee::getEmployeeName() {return employeeName;}
void CompanyEmployee::setEmployeeName(CString name) {employeeName = name ;}
void CompanyEmployee::update(AddressBook* book){
setAddressBook(book);
addressBookContents = addressBook->getAddressBook();
printf(getEmployeeName()+"更新通訊錄。"+"通訊錄內容:"+addressBookContents +"\n");
}
觀察者模式測試程序的代碼清單12所示。其實現的序列圖如圖05所示。
代碼清單12
#include "stdafx.h"
#include "Employee.h"
#include "AddressBook.h"
int main(int argc, char* argv[]){
printf("——————原有的通訊錄——————\n");
AddressBook* addressBook = new CompanyAddressBook();
Employee* employee1 = new CompanyEmployee("employee1");
Employee* employee2 = new CompanyEmployee("employee2");
addressBook->addEmployee(employee1);
addressBook->addEmployee(employee2);
addressBook->notice();
printf("——————更新的通訊錄——————\n");
CString newAddressBook = "新的通訊錄";
addressBook->setAddressBook(newAddressBook);
addressBook->notice();
delete employee1,employee2,addressBook;
return 0;
}
觀察者模式測試類輸出結果如下所示:
——————原有的通訊錄——————
employee1更新通訊錄。通訊錄內容:舊的通信錄
employee2更新通訊錄。通訊錄內容:舊的通信錄
——————更新的通訊錄——————
employee1更新通訊錄。通訊錄內容:新的通訊錄
employee2更新通訊錄。通訊錄內容:新的通訊錄
6 擴展和說明
在java語言中已經實現了Observer設計模式。JDK裡提供的Observer設計模式的實現由java.util.Observable類和java.util.Observer接口組成。java.util.Observer是觀察者角色,java.util.Observable是被觀察目標角色,對應於Subject角色。
Observer是一個接口,它裡面只定義了一個方法:public void update(Observable obs,Object obj)在用戶實現的這個方法中,可以只對obs的某些事件進行響應,也可以通過調用getXXX()來得到obs最新的狀態。上述例子的java程序可以由Employee類、AddressBook類、Client類等三個文件實現。
AddressBook類源程序見程序清單08。
程序代碼清單08
import java.util.Observable;
public class AddressBook extends Observable {
protected String addressBookContent;
public AddressBook(){super();}
public String getAddressBook() {return addressBookContent;
public void setAddressBook(String addressBook) {this.addressBookContent = addressBook;}
public void addEmployee(Employee employee){super.addObserver(employee); }
public void removeEmployee(Employee employee){deleteObserver(employee);
public void modifyAddress(String addressBook){
setAddressBook(addressBook);
setChanged();
notifyObservers(addressBook);
}
}
Employee.類源程序見程序清單09。
程序代碼清單08
import java.util.Observable;
import java.util.Observer;
public class Employee implements Observer {
protected String addressBookContents;
private String employeeName ;
public Employee(String employeeName){setEmployeeName(employeeName);}
public String getEmployeeName() {return employeeName;}
public void setEmployeeName(String employeeName) {this.employeeName = employeeName;}
public String getAddressBookContents() {return addressBookContents;}
public void setAddressBookContents(String addressBookContents) {
this.addressBookContents = addressBookContents;
}
public void update( Observable object,Object arg ){
if(arg instanceof String){
setAddressBookContents(arg.toString());
System.out.println(getEmployeeName()+"獲得通信錄:"+getAddressBookContents());
}
}
}
Client類源程序見程序清單10。
程序代碼清單10
public class Client {
public static void main(String[] args) {
System.out.println("——————原有的通訊錄——————");
AddressBook addressBook = new AddressBook();
Employee employee1 = new Employee("employee1");
Employee employee2 = new Employee("employee2");
Employee employee3 = new Employee("employee3");
addressBook.addEmployee(employee1);
addressBook.addEmployee(employee2);
addressBook.addEmployee(employee3);
addressBook.modifyAddress("舊的通訊錄");
System.out.println("——————更新的通訊錄——————");
String newAddressBook = "新的通訊錄";
addressBook.modifyAddress(newAddressBook);
}
}
參考文獻
[1] E.Gamma, R.Helm, R.Johnson, and Vlissides. Design Patterns Elements of Reusable Object Oriented Software. Addison-Wesley, 1995
[2] E.Gamma, R.Helm, R.Johnson, and Vlissides.著,李英軍等譯,設計模式:可復用面向對象軟體的基礎,北京:機械工業出版社.2000.9.
[3] 閻宏,Java與模式,北京:電子工業出版社. 2002.10
[4] 王俊峰戚曉濱. 設計模式和UML. 計算機應用研究,1999.16(5), 27-29,39.
[5] 陳琴朱正強. UML在設計模式描述中的應用. 計算機工程與設計,2003.24(4), 81-84.
[6] 吳信永宋東劉飛. 基於構件技術的通用ATS框架設計. 計算機測量與控制,2008.16(2), 141-143,155.
[7] 趙德新劉瑾. 設計模式思想及其應用. 天津理工大學學報,2007.23(5), 58-61.
[8] 閻小濤劉濤沈為群. 設計模式在航空發動機試驗臺測控系統中的應用. 兵工自動化,2007.26(10), 62-64.
[9] 張光會閆新慶. Java2中Observer模式探究. 南陽師範學院學報,2002.1(6), 80-82,92.
[10] 張寧王越王東. 觀察者模式及其在軟體開發中的應用. 大眾科技,2008.(11), 35-36.
[11] 吳善明沈建京劉輝. 淺析Observer模式在GIS軟體設計中的應用. 計算機工程與設計,2007.28(18), 4532-4535.
[12] 董榮輝丁政建曾天慧. GoF23中Observer設計模式的AOP實現與OOP實現的對比. 微計算機信息,2006.(05X), 225-227,219.
[13] 王雪濤樊銀亭. Observer模式在.net架構中的應用研究. 華北水利水電學院學報,2008.29(4), 79-81,84.
[14] 雷鎮雷蕾周淑秋王華. Observer模式在JavaGUI中的分析與應用. 計算機系統應用,2005.(5), 32-34.
[15] 王江濤. 觀察者設計模式在嵌入式系統中的應用. 計算機工程,2004.30(B12), 66-68,135.
[16] 張曉東閆新慶等. Observer軟體設計模式在Delphi中的實現. 河南師範大學學報:自然科學版,2001.29(4), 85-87.
[17] 周傳宏吳思達. 可擴展的基於設計模式的PLM定製架構. 製造業自動化,2007.29(4), 27-29,76.
[18] 楊洲王自強周餘都思丹. 設計模式在數據採集系統中的應用. 微電子學與計算機,2008.25(2), 107-110.
[19] 趙永明郭敏. 設計模式在雷達終端軟體系統中的應用. 火控雷達技術,2008.(2), 93-96.
[20] 王曉波蔡漢明王軍崔慧敏. 設計模式在線切割CAD軟體開發中的應用. 機電工程技術,2006.35(2),44-47.
[21] 萬波張焰羅治強. 設計模式在Drawcli程序中的應用. 計算機應用與軟體,2003.20(8), 12-13,84.
[22] 李長春廖建新王純朱曉民. 軟體界面國際化及設計模式的應用. 北京工商大學學報:自然科學版,2007.25(5), 53-56.
[23] 唐露萍陳潔. 設計模式在短波通信軟體中的應用. 計算機工程,2008.34(D09), 148-150.
[24] 何劍峰雷金輝高潤琴. 設計模式在真空爐控制系統中的應用. 工業控制計算機,2008.21(3), 42-42,45.
[25] 張曉懷陳業初寇蔚. 設計模式在設備故障紅外智能診斷軟體中的應用. 機電設備,2007.24(11), 29-33,13.
[26] 洪中. 組態軟體設計中的模式應用. 微計算機信息,2007.(28), 279-281.
[27] 肖保良宋東趙勝. 基於模式的ATS領域框架研究. 計算機測量與控制,2007.15(9), 1144-1147.
[28] 曹恆凌正陽蔣知峰王劍蘭. 軟體設計模式在數控系統人機界面開發中的應用. 華東理工大學學報:社會科學版,2007.33(6), 878-882.
[29] 何江玲孫其博. 設計模式在軟交換軟體系統中的應用. 世界電信,2003.16(9), 42-44.
[30] 肖志峰龔健雅王豔東翟曉芳. 面向對象的軟體設計模式在配電GIS中的應用. 測繪信息與工程,2005.30(3), 3-5.
[31] 袁杲楊玲. Observer與Command模式在VTK類庫設計中的應用研究. 西南民族大學學報:自然科學版,2007.33(4), 972-975.
[32] 戴建國郭理曹傳東. JUnit框架剖析. 計算機與數字工程,2008.36(8), 43-45,135.
[33] 肖計劃劉海硯張吉才. 設計模式在地圖製圖軟體開發中的應用. 測繪工程,2008.17(5), 4-7,11.
[34] 聶穎. 設計模式在圖形處理軟體中的應用. 計算機應用,2004.24(B12), 189-191.
[35] 饒一梅王治寶. 軟體設計模式及其在Java類庫中的典型實現. 計算機工程與應用,2002.38(4), 48-50.