求解Java中观察者模式的实际运用 java观察者模式 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 嗯,说的没错。但是目前的问题是:在观察者那端(也就是我这里的B项目),怎么讲自己添加到主题端的观察者列表中呢,目前这块比较难懂管理观察者的类应该提供注册成观察者的方法的无非就是List<IObserver > lstObserver = new ArrayList<IObserver >();public void register(IObserver ob){ lstObserver .add(ob);}保证这个方法B项目能调用到就行了 我现在参照上面的想法整理了一下伪代码:---------------A 项目------@Servicepublic class ConfigServiceImpl extends ServiceImplBase { // 该方法会通过一定时器定时查询并返回当前服务状态,即ServiceStatusBean public ServiceStatusBean registerService(ServiceConfigBean config) throws TException { //注册服务配置 ServiceStatusBean serviceStatusBean = serviceConfigHander.registerConfig(config); System.out.println("观察者共有: " + this.getWatcherList().size()); // 这里始终都是 0,也就是说B观察者没有注册进来 return serviceStatusBean; }}---------------B项目---------------@Servicepublic class TimeStampServiceImpl extends ServiceImplBase implements IObserver { public TimeStampServiceImpl (){ this.regist(this); }}然后,我在这俩项目公有继承的类ServiceImplBase 中,加入了主题的代码:----------------ServiceImplBase.java----------------public class VRVServiceImplBase implements Subject{ private List<IObserver> watcherList = new ArrayList<IObserver>(); @Override public void AddWatcher(IObserver o) { watcherList.add(o); } @Override public void DelWatcher(IObserver o) { watcherList.remove(o); } @Override public void NotifyObserver() { for(IObserver o: watcherList){ watcher.Update(); } } public List<IObserver> getWatcherList() { return watcherList; }}这样在A里边始终取到的观察者列表数据都是 0,不知道为毛线,求指教 B要向A注册?这还不简单,B把自己写进一个共享文件里给A读,写进公共数据库里给A读,写进公共缓存服务器里给A读,写进公共的配置服务器里给A读。挑一个吧 更正一下上面B项目中的伪代码:---------------B项目---------------@Servicepublic class TimeStampServiceImpl extends ServiceImplBase implements IObserver { public TimeStampServiceImpl (){ this.AddWatcher(this); }} 你不在同一个项目里,是不能那样注册的。两个项目要共享数据,按照我上面的描述去考虑不需要这么麻烦,其实Java的观察者模式完全可以做到,你可以去了解一下GOF - 观察者模式 两个项目,一般会部署在不同的服务器上。(即使在相同的服务器上也可能运行在不同JVM进程中,即使同一个JVM中,也可能是不同的ClassLoader对象进行的类加载。)所以,楼主,这里缺少一个通信机制。将消息传递给相关联的项目。如果是同一个项目中的两个不同的功能模块,那么,谈起模式来,这个模型最起码是简单的,考虑得东西也相对少些。 嗯,没错,其实就是同一个项目中的两个不同模块,只是这两个模块各自是一个project(通过Maven管理)便于维护嘛。能否给段伪代码 嗯,没错,其实就是同一个项目中的两个不同模块,只是这两个模块各自是一个project(通过Maven管理)便于维护嘛。能否给段伪代码已经告诉你了两个项目的共享数据没那么简单B把自己写进一个共享文件里给A读或者写进公共数据库里给A读或者写进公共缓存服务器里给A读或者写进公共的配置服务器里给A读。 没有帮忙啊我再重新描述一下问题吧:项目A中定时每秒更改bean(即 ServiceStatusBean)的值,项目B需要实时取该bean的值,该咋如何解决???ps:目前在项目B中也定时器了一下,貌似这样有很大的风险,很容易有时间差,不太科学 大致代码如下,实际应用请自己修改。public interface MyChangeListener extends java.beans.PropertyChangeListener{}@Servicepublic class ConfigServiceImpl extends ServiceImplBase { // 该方法会通过一定时器定时查询并返回当前服务状态,即ServiceStatusBean public ServiceStatusBean registerService(ServiceConfigBean config) throws TException { //注册服务配置 ServiceStatusBean serviceStatusBean = serviceConfigHander.registerConfig(config); return serviceStatusBean; } public void doSomeService(){ //Do something //Trigger Event support.firePropertyChange("", 1, 2); } private java.beans.PropertyChangeSupport support; @PostContruct public void init(ApplicationContext context){ support = new java.beans.PropertyChangeSupport(); Map<String, MyChangeListener> beans = context.getBeansOfType(MyChangeListener.class); for(Map.Entry<String, MyChangeListener> entry : beans.entrySet()){ support.addPropertyChangeListener(entry.getValue()); } }}@Servicepublic class TimeStampServiceImpl extends ServiceImplBase implements MyChangeListener{ public void propertyChange(PropertyChangeEvent evt){ Object newVal = evt.getNewValue(), oldValue = evt.getOldValue(); String propertyName = evt.getPropertyName(); //TODO something for this event. }} 刚看到你是2个工程的项目要相互通信。比较优雅的方案有2个。第一种:B项目对外暴露一个webservice,A项目属性变动时访问此WebService即可通知。第二种:增加一个JMS服务容器,A项目建立一个订阅者的消息队列。B项目去监听A项目的消息队列。A有情况时发送消息到队列B即可收到,并做相应处理。注:此方法可以多个工程同时监听A的消息队列,也就是说谁关心A的变动谁就去监听A的消息队列。个人比较倾向于第二种方法。 嗯,首先非常感谢。仔细看了下你的第二种方案,貌似可以满足一定的需求,不过JMS没怎么用过。能否贴一段伪代码,加强理解一下第二种方案的思路,然后好有个研究的方向。 A项目 添加针对状态改变的监听器对象B项目 实现监听器的操作示例代码: /** * 状态变化监听器,当状态变化时出发监听器的方法 */ public interface StatusChangeListener{ /** * 当前后两种状态发生变化时,触发该方法的调用 * @param lastStatus 上一次的状态 * @param currentStatus 当前状态 */ void onStatusChanged(ServiceStatusBean lastStatus, ServiceStatusBean currentStatus); } @Service public class ConfigServiceImpl extends ServiceImplBase { private ServiceStatusBean lastStatus; private List<StatusChangeListener> listeners = new ArrayList<StatusChangeListener>(); // 该方法会通过一定时器定时查询并返回当前服务状态,即ServiceStatusBean public ServiceStatusBean registerService(ServiceConfigBean config) throws TException { // 注册服务配置 ServiceStatusBean serviceStatusBean = serviceConfigHander.registerConfig(config); if(isChanged(lastStatus,serviceStatusBean)){ fireStatusChanged(lastStatus,serviceStatusBean); } lastStatus = serviceStatusBean; return serviceStatusBean; } /** * 比较两次状态,判断状态是否改变了 * @param lastStatus 上一次的状态值 * @param currentStatus 当前状态值 * @return 状态是否发生变化 */ private boolean isChanged(ServiceStatusBean lastStatus, ServiceStatusBean currentStatus) { // TODO 实现状态是否发生变化的判断 return false; } /** * 当前后两种状态发生变化时,触发该方法的调用 * @param lastStatus 上一次的状态 * @param currentStatus 当前状态 */ private void fireStatusChanged(ServiceStatusBean lastStatus, ServiceStatusBean currentStatus) { for(StatusChangeListener listener : listeners){ listener.onStatusChanged(lastStatus, currentStatus); } } /** * 添加(注册)监听器对象,可以注册多个对象。 */ public void addStatusChangeListener(StatusChangeListener listener){ listeners.add(listener); } } @Service public class TimeStampServiceImpl extends ServiceImplBase implements StatusChangeListener{ // 此处待实现 public void onStatusChanged(ServiceStatusBean lastStatus, ServiceStatusBean currentStatus) { // TODO 此处实现,当ServiceStatusBean状态发生变化时,对应的操作内容。 } }程序初始化的时候,想办法调用 ConfigServiceImpl 的 addStatusChangeListener 方法 (TimeStampServiceImpl 对象作为参数),就可以完成楼主提出的功能了。 嗯,一直想把这种方案利用起来,貌似难点都在你那个没说明白的上面,怎么讲B工程中的TimeStampServiceImpl 添加到A工程的ConfigServiceImpl 中addStatusChangeListener 来是个关键 楼主的代码,实现类中都没有对应的接口类,所以,我这里也只能顺着用了。@Service public class TimeStampServiceImpl extends ServiceImplBase implements StatusChangeListener{ @Resource private ConfigServiceImpl configServiceImpl; @PostConstruct public void init(){ configServiceImpl.addStatusChangeListener(this); } // 此处待实现 public void onStatusChanged(ServiceStatusBean lastStatus, ServiceStatusBean currentStatus) { // TODO 此处实现,当ServiceStatusBean状态发生变化时,对应的操作内容。 } } 每个系统的都会有一个初始化的过程,一般都会专门写一些代码来完成初始化的过程。我写的这个代码,虽然能够完成楼主的要求,但是,不太符合项目的整体规划。具体怎么实现,选用哪种方案实现,要问项目经理或者技术经理的。如果这个项目是你用来练手或者熟悉Spring、熟悉观察者模式的,那么,随便怎么折腾都好。 额我是不是没表达清楚,这里的ConfigServiceImpl 和 TimeStampServiceImpl 这俩类是分属于不同的java project里边的,而且都不是web工程,属于是java project,最终是通过jvm加载main运行的,所以你这里在TimeStampServiceImpl类里边引用ConfigServiceImpl是引用不到的,就算引用到了,也不再同一个进程里边吧(个人想法有不对的地方请指正) 如何判断一个String是json格式还是XML格式 java日期验证 下面的代码编译不动~~~ 代码有没问题啊? 嗷嗷!!!!!!! 如何对JavaBean的某两个属性排序? 如何取得类似于按delta函数分布的随机数? 请指教--关于JTable的刷新问题 怎样在jList中,双击一个某项,则弹出另一个窗口 如何使背景透明? attfile.length()为什么不论多大的文件,输出的为什么总是0呀 java.sql.BatchUpdateException: Io exception: Broken pipe 关于clone返回值的疑惑 新手String类
但是目前的问题是:在观察者那端(也就是我这里的B项目),怎么讲自己添加到主题端的观察者列表中呢,目前这块比较难懂
管理观察者的类应该提供注册成观察者的方法的
无非就是
List<IObserver > lstObserver = new ArrayList<IObserver >();
public void register(IObserver ob){
lstObserver .add(ob);
}
保证这个方法B项目能调用到就行了
@Service
public class ConfigServiceImpl extends ServiceImplBase {
// 该方法会通过一定时器定时查询并返回当前服务状态,即ServiceStatusBean
public ServiceStatusBean registerService(ServiceConfigBean config) throws TException {
//注册服务配置
ServiceStatusBean serviceStatusBean = serviceConfigHander.registerConfig(config);
System.out.println("观察者共有: " + this.getWatcherList().size()); // 这里始终都是 0,也就是说B观察者没有注册进来
return serviceStatusBean;
}
}---------------B项目---------------
@Service
public class TimeStampServiceImpl extends ServiceImplBase implements IObserver {
public TimeStampServiceImpl (){
this.regist(this);
}
}然后,我在这俩项目公有继承的类ServiceImplBase 中,加入了主题的代码:
----------------ServiceImplBase.java----------------
public class VRVServiceImplBase implements Subject{
private List<IObserver> watcherList = new ArrayList<IObserver>();
@Override
public void AddWatcher(IObserver o) {
watcherList.add(o);
}
@Override
public void DelWatcher(IObserver o) {
watcherList.remove(o);
}
@Override
public void NotifyObserver() {
for(IObserver o: watcherList){
watcher.Update();
}
} public List<IObserver> getWatcherList() {
return watcherList;
}
}
这样在A里边始终取到的观察者列表数据都是 0,不知道为毛线,求指教
@Service
public class TimeStampServiceImpl extends ServiceImplBase implements IObserver {
public TimeStampServiceImpl (){
this.AddWatcher(this);
}
}
你不在同一个项目里,是不能那样注册的。
两个项目要共享数据,按照我上面的描述去考虑
不需要这么麻烦,其实Java的观察者模式完全可以做到,你可以去了解一下GOF - 观察者模式
所以,楼主,这里缺少一个通信机制。将消息传递给相关联的项目。如果是同一个项目中的两个不同的功能模块,那么,谈起模式来,这个模型最起码是简单的,考虑得东西也相对少些。
能否给段伪代码
能否给段伪代码已经告诉你了两个项目的共享数据没那么简单B把自己写进一个共享文件里给A读
或者写进公共数据库里给A读
或者写进公共缓存服务器里给A读
或者写进公共的配置服务器里给A读。
项目A中定时每秒更改bean(即 ServiceStatusBean)的值,项目B需要实时取该bean的值,该咋如何解决???ps:目前在项目B中也定时器了一下,貌似这样有很大的风险,很容易有时间差,不太科学
}@Service
public class ConfigServiceImpl extends ServiceImplBase {
// 该方法会通过一定时器定时查询并返回当前服务状态,即ServiceStatusBean
public ServiceStatusBean registerService(ServiceConfigBean config) throws TException {
//注册服务配置
ServiceStatusBean serviceStatusBean = serviceConfigHander.registerConfig(config);
return serviceStatusBean;
} public void doSomeService(){
//Do something
//Trigger Event
support.firePropertyChange("", 1, 2);
} private java.beans.PropertyChangeSupport support; @PostContruct
public void init(ApplicationContext context){
support = new java.beans.PropertyChangeSupport();
Map<String, MyChangeListener> beans = context.getBeansOfType(MyChangeListener.class);
for(Map.Entry<String, MyChangeListener> entry : beans.entrySet()){
support.addPropertyChangeListener(entry.getValue());
}
}
}
@Service
public class TimeStampServiceImpl extends ServiceImplBase implements MyChangeListener{
public void propertyChange(PropertyChangeEvent evt){
Object newVal = evt.getNewValue(), oldValue = evt.getOldValue();
String propertyName = evt.getPropertyName();
//TODO something for this event.
}
}
比较优雅的方案有2个。
第一种:B项目对外暴露一个webservice,A项目属性变动时访问此WebService即可通知。
第二种:增加一个JMS服务容器,A项目建立一个订阅者的消息队列。B项目去监听A项目的消息队列。A有情况时发送消息到队列B即可收到,并做相应处理。注:此方法可以多个工程同时监听A的消息队列,也就是说谁关心A的变动谁就去监听A的消息队列。
个人比较倾向于第二种方法。
仔细看了下你的第二种方案,貌似可以满足一定的需求,不过JMS没怎么用过。
能否贴一段伪代码,加强理解一下第二种方案的思路,然后好有个研究的方向。
B项目 实现监听器的操作
示例代码: /**
* 状态变化监听器,当状态变化时出发监听器的方法
*/
public interface StatusChangeListener{
/**
* 当前后两种状态发生变化时,触发该方法的调用
* @param lastStatus 上一次的状态
* @param currentStatus 当前状态
*/
void onStatusChanged(ServiceStatusBean lastStatus, ServiceStatusBean currentStatus);
}
@Service
public class ConfigServiceImpl extends ServiceImplBase {
private ServiceStatusBean lastStatus;
private List<StatusChangeListener> listeners = new ArrayList<StatusChangeListener>();
// 该方法会通过一定时器定时查询并返回当前服务状态,即ServiceStatusBean
public ServiceStatusBean registerService(ServiceConfigBean config) throws TException {
// 注册服务配置
ServiceStatusBean serviceStatusBean = serviceConfigHander.registerConfig(config);
if(isChanged(lastStatus,serviceStatusBean)){
fireStatusChanged(lastStatus,serviceStatusBean);
}
lastStatus = serviceStatusBean;
return serviceStatusBean;
}
/**
* 比较两次状态,判断状态是否改变了
* @param lastStatus 上一次的状态值
* @param currentStatus 当前状态值
* @return 状态是否发生变化
*/
private boolean isChanged(ServiceStatusBean lastStatus, ServiceStatusBean currentStatus) {
// TODO 实现状态是否发生变化的判断
return false;
}
/**
* 当前后两种状态发生变化时,触发该方法的调用
* @param lastStatus 上一次的状态
* @param currentStatus 当前状态
*/
private void fireStatusChanged(ServiceStatusBean lastStatus, ServiceStatusBean currentStatus) {
for(StatusChangeListener listener : listeners){
listener.onStatusChanged(lastStatus, currentStatus);
}
}
/**
* 添加(注册)监听器对象,可以注册多个对象。
*/
public void addStatusChangeListener(StatusChangeListener listener){
listeners.add(listener);
}
} @Service
public class TimeStampServiceImpl extends ServiceImplBase implements StatusChangeListener{
// 此处待实现
public void onStatusChanged(ServiceStatusBean lastStatus, ServiceStatusBean currentStatus) {
// TODO 此处实现,当ServiceStatusBean状态发生变化时,对应的操作内容。
}
}程序初始化的时候,想办法调用 ConfigServiceImpl 的 addStatusChangeListener 方法 (TimeStampServiceImpl 对象作为参数),就可以完成楼主提出的功能了。
@Service
public class TimeStampServiceImpl extends ServiceImplBase implements StatusChangeListener{
@Resource
private ConfigServiceImpl configServiceImpl;
@PostConstruct
public void init(){
configServiceImpl.addStatusChangeListener(this);
}
// 此处待实现
public void onStatusChanged(ServiceStatusBean lastStatus, ServiceStatusBean currentStatus) {
// TODO 此处实现,当ServiceStatusBean状态发生变化时,对应的操作内容。
}
}
我写的这个代码,虽然能够完成楼主的要求,但是,不太符合项目的整体规划。
具体怎么实现,选用哪种方案实现,要问项目经理或者技术经理的。
如果这个项目是你用来练手或者熟悉Spring、熟悉观察者模式的,那么,随便怎么折腾都好。
我是不是没表达清楚,这里的ConfigServiceImpl 和 TimeStampServiceImpl 这俩类是分属于不同的java project里边的,而且都不是web工程,属于是java project,最终是通过jvm加载main运行的,所以你这里在TimeStampServiceImpl类里边引用ConfigServiceImpl是引用不到的,就算引用到了,也不再同一个进程里边吧(个人想法有不对的地方请指正)