分享
 
 
 

观察者模式组图(Observer Pattern)

王朝other·作者佚名  2008-05-31
窄屏简体版  字體: |||超大  

描述:

在设计一组依靠的对象与它们所依靠的对象之间一致(同步)的交流模型时,观察者模式(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类不需要任何变化。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有