本文我们介绍观察者模式,通过Java语言提供多种方法实现。
什么是观察者模式
观察者模式属于行为设计模式,用于对象之间交互:可观察对象和观察者。可观察对象当其状态发生变化时通知观察者。举例,新闻机构收到新闻时通知频道,接收新闻表示新闻机构的状态发生了变化,触发频道接收通知。
下面看如何实现。首先定义新闻机构:
public class NewsAgency {
private String news;
private List<Channel> channels = new ArrayList<>();
public void addObserver(Channel channel) {
this.channels.add(channel);
}
public void removeObserver(Channel channel) {
this.channels.remove(channel);
}
public void setNews(String news) {
this.news = news;
for (Channel channel : this.channels) {
channel.update(this.news);
}
}
}
NewsAgency
是可观察对象,当有新闻更新时,它的状态变化了;新闻机构通过调用update()方法通知观察者这个事实。要实现该功能,可观察对象需要持有观察者的引用,上面示例为channels变量。
现在我们看观察者,Channel类,它应该有update方法,当 NewsAgency
状态变化时调用该方法。
public class NewsChannel implements Channel {
private String news;
@Override
public void update(Object news) {
this.setNews((String) news);
}
}
Channel接口仅有一个方法。
public interface Channel {
public void update(Object o);
}
现在如果增加 NewsChannel
至观察者列表,改变 NewsAgency
状态, NewsChannel
实例会调用update方法:
NewsAgency observable = new NewsAgency();
NewsChannel observer = new NewsChannel();
observable.addObserver(observer);
observable.setNews("news");
assertEquals(observer.getNews(), "news");
java 核心库中已经预定义了 Observer 接口,利用它实现观察者模式会更简单,下面看看如何利用。
利用 Observer
接口实现
java.util.Observer
接口定义了update方法,所以无需定义前一节的接口。下面看如何使用
public class ONewsChannel implements Observer {
private String news;
@Override
public void update(Observable o, Object news) {
this.setNews((String) news);
}
}
第二个参数来自Observable对象,请继续往下浏览:
为了定义可观察者对象,需要继承 Observable 类:
public class ONewsAgency extends Observable {
private String news;
public void setNews(String news) {
this.news = news;
setChanged();
notifyObservers(news);
}
}
注意我们不需要直接调用 update 方法,仅需要调用 setChanged()
和 notifyObservers
() 方法,剩下的事情有 Observable 类负责。
同样它也维护一组观察者,并暴露方法维护该列表—— addObserver()
和 deleteObserver()
。为了测试结果,我们需要增加观察者至列表并设置新闻:
ONewsAgency observable = new ONewsAgency();
ONewsChannel observer = new ONewsChannel();
observable.addObserver(observer);
observable.setNews("news");
assertEquals(observer.getNews(), "news");
Observer 接口从java 9 开始已不建议使用 (标记为 deprecated )。其中一个问题是 Observable 不是接口而是类,这就是为什么子类不能作为Observable使用的原因。另外开发者可能会覆盖Observable 的同步方法破坏了线程安全。
下面我们看 PropertyChangeListener
接口,它被推荐待用 Observer 。
利用 PropertyChangeListener
实现
该方法可观测对象必须持有 PropertyChangeListener
实例,当类属性变化是用其发送通知给观察者。
public class PCLNewsAgency {
private String news;
private PropertyChangeSupport support;
public PCLNewsAgency() {
support = new PropertyChangeSupport(this);
}
public void addPropertyChangeListener(PropertyChangeListener pcl) {
support.addPropertyChangeListener(pcl);
}
public void removePropertyChangeListener(PropertyChangeListener pcl) {
support.removePropertyChangeListener(pcl);
}
public void setNews(String value) {
support.firePropertyChange("news", this.news, value);
this.news = value;
}
}
使用该方法,也可以增加或删除观察者,当可观察对象状态变化是发送通知:
support.firePropertyChange("news", this.news, value);
第一个参数是属性名称,后面两个参数分别是原值和新值。观察者需要实现 PropertyChangeListener
接口:
public class PCLNewsChannel implements PropertyChangeListener {
private String news;
public void propertyChange(PropertyChangeEvent evt) {
this.setNews((String) evt.getNewValue());
}
}
由于 propertychangessupport
类是我们自己实现,故可以从事件中获取新的属性值。下面测试看结果:
PCLNewsAgency observable = new PCLNewsAgency();
PCLNewsChannel observer = new PCLNewsChannel();
observable.addPropertyChangeListener(observer);
observable.setNews("news");
assertEquals(observer.getNews(), "news");
总结
本文介绍了观察者模式,在Java中提供三种方式进行实现,其中推荐使用 PropertyChangeListener
方法。
本文参考链接:https://blog.csdn.net/neweastsun/article/details/118163356