本文的观察者模式实例 是使用java.beans库里提供的PropertyChangeSupport与PropertyChangeListener编写,想直接查看PropertyChangeSupport与PropertyChangeListener的基本介绍和使用方法的,请直接翻到第二点查看。笔者现在本科初学软件设计模式,如有不对之处,请广大读者评论区赏脸指正,本人马上更新修改文章。如果觉得还可以,那就点一个赞,非常感谢!笔者希望和大家共同进步。
一、观察者模式(Observer Pattern)
1、模式动机与模式意图
(1)模式动机
* 将一个系统分割成一系列相互协作的类有一个常见的副作用:需要维护相关对象间的一致性。我们不希望为了维持一致性而使得各个类紧密耦合,导致可重用性的降低。
* 观察者模式使得任意数目的观察者不必知道彼此的存在,且主题发生变化时都可以得到主题的通知,而同步改变状态。是一种很松的耦合。具有很好的可重用性。
(2)模式意图
定义对象间的一种一对多的依赖关系。当一方的对象改变状态时,所有的依赖者都会得到通知并被自动更新。
2、观察者模式的定义
观察者模式定义了对象间的一对多依赖关系。当一方的对象改变状态时,所有的依赖者都会被通知并自动被更新。
在观察者模式中,被依赖的一方叫做目标或主题(Subject),依赖方叫做观察者(Observers)。
观察者模式一种更好理解的名字是:发布-订阅模式(Publish-Subscribe)
主题与观察者之间的关系如下所示:
对抽象编程,而不是对实现编程。引入高级接口(把相同的东西抽象出来)
3、高质量设计的原则---松耦合(loose Coupling)
* 如果两个对象是松耦合的,则他们可以相互作用,但彼此的依赖性很小。
* 观察者模式符合松耦合的原则。因为:
* 主题(subject)只需要知道其观察者(Observer)实现了某个接口。
* 可以随时加入观察者。
* 不需要修改主题就可以加入新的类型的观察者
* 主题和观察者都可以独立地被复用
* 修改主题或观察者都不会影响另一方。
* 观察者之间互不相干。
4、在JAVA语言的java.util库里面,提供了一个Observable类以及一个Observer接口,构成JAVA语言对观察者模式的支持。(JAVA9已弃用,在此只是简略介绍,本文重点介绍的PropertyChangeSupport和PropertyChangeListener在下方第二点)
public interface Observer {
//第一参数是被观察者(主题)对象,第二个参数是方法所需要的参数
void update(Observable o, Object arg);
}
被观察者类都是java.util.Observable类的子类。java.util.Observable提供公开的方法支持观察者对象,这些方法中有两个对Observable的子类非常重要:一个是setChanged(),另一个是notifyObservers()。第一方法setChanged()被调用之后会设置一个内部标记变量,代表被观察者对象的状态发生了变化。第二个是notifyObservers(),这个方法被调用时,会调用所有登记过的观察者对象的update()方法,使这些观察者对象可以更新自己。以下是Observable类的方法列表
Observable()
addObserver(Observer o)
deleteObserver (Observer o)
notifyObservers()
notifyObservers(Object arg)
deleteObservers
setChanged()
clearChanged()
hasChanged()
countObservers()
类图如下:
5、观察者模式的优缺点
(1)优点
* 观察者模式可以实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色。
* 观察者模式在观察目标和观察者之间建立一个抽象的耦合。
* 观察者模式支持广播通信。
* 观察者模式符合“开闭原则”的要求。
(2)缺点
* 如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
* 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
* 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
6、Observer模式的适用性
(1)当一个抽象模型有两个方面,其中一个方面依赖于另一个方面时,将这两者封装在独立的对象中使他们可以独立的改变和复用。
(2)Observer模式的运用场合:
* 一个对象的改变需要同时改变其他对象,但不知道具体有多少其它对象需要改变。
* 一个对象必须通知其他对象,而他又不能预先知道其它对象是谁。
7、小结:三大模式中的行为模式
* 意图:给出一种提供灵活行为的方式
* 基本思想:分离变化、并单独封装变化
* 模式实例:Strategy,Observer
* Observer模式属于行为型。不少人认为Observer模式是“解耦型模式”(降低对象间的耦合度)的最佳范例。
//当绑定属性发生更改时,将自动调用此方法。
/**
* This method gets called when a bound property is changed.
* @param evt A PropertyChangeEvent object describing the event source
* and the property that has changed.
*/
void propertyChange(PropertyChangeEvent evt);
作用:接受一个event(被监听者状态改变自动产生的PropertyChangeEvent),然后根据这个event做点反应。
public PropertyChangeSupport(Object sourceBean)
构造函数的一般使用方法如下:
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
public void addPropertyChangeListener(PropertyChangeListener listener)
public void addPropertyChangeListener(PropertyChangeListener listener){
this.pcs.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener)
public void firePropertyChange(String propName, Object oldValue, Object newValue) {
this.pcs.firePropertyChange(propName, oldValue, newValue);
}
public void firePropertyChange(String propertyName, Object oldValue, Object newValue)
public void firePropertyChange(String propertyName, int oldValue, int newValue)
public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue)
public void firePropertyChange(PropertyChangeEvent event)
public String getPropertyName()
public Object getNewValue()
public Object getOldValue()
public Object getSource()
package Observer;
import com.google.protobuf.Internal;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
//操控股市变化的类
public class StockMarketChange {
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
//记录历史的所有股市价格
private List price;
//趋势预测
private boolean tendency;
//统计分析报告
private StatisticalData statisticalData;
/**
* 股市初始化
*/
public StockMarketChange() {
this.price=new ArrayList<Double>();
Random random = new Random();
this.price.add((double)random.nextFloat()*100);
//更新趋势预测
int number = random.nextInt(2);
if (number==1) {
this.tendency = true;
}
else {
this.tendency = false;
}
this.statisticalData=new StatisticalData(getNewPrice(),getNewPrice(),getNewPrice());
}
/**
* 让股市价格变化并更新价格
*/
private void changeNewPrice(){
Random random = new Random();
//改变后的值
double newPrice = (double) random.nextFloat()*100;
//改变前的值
double oldPrice = (double)this.price.get(this.price.size()-1);
//添加改变的值
this.price.add(newPrice);
firePropertyChange("股市价格发生变动",oldPrice,newPrice);
}
/**
* 更新趋势预测
*/
private void updateTendency(){
if(this.price.size()>1) {
double newPrice = (double)this.price.get(this.price.size()-1);
double oldPrice = (double)this.price.get(this.price.size()-2);
String oldTendency=getTendency();
if(newPrice>=oldPrice){
this.tendency = true;
}else {
this.tendency = false;
}
String newTendency=getTendency();
if(!newTendency.equals(oldTendency)){
firePropertyChange("股市趋势预测发生变动",oldTendency,newTendency);
}
}
}
/**
* 更新统计分析报告
*/
private void updateStatisticalData(){
if(this.price.size()>1){
StatisticalData oldStatisticalData=new StatisticalData(this.statisticalData);
StatisticalData newStatisticalData=this.statisticalData;
double max= (double) Collections.max(this.price);
double min= (double) Collections.min(this.price);
double average=0;
for(int i=0;i<this.price.size();i++){
average=average+(double)this.price.get(i);
}
average=average/this.price.size();
newStatisticalData.setAverage(average);
newStatisticalData.setMax(max);
newStatisticalData.setMin(min);
firePropertyChange("统计分析报告发生变动",oldStatisticalData,newStatisticalData);
}
}
/**
* 控制股市变化的函数
*/
public void change(){
changeNewPrice();
updateTendency();
updateStatisticalData();
}
/**
* 获得趋势预测情况
* @return
*/
public String getTendency() {
if(this.tendency){
return "股市价格将上涨";
}else {
return "股市价格将下跌";
}
}
/**
* 获得统计分析报告
* @return
*/
public StatisticalData getStatisticalData() {
return statisticalData;
}
/**
* 获取股市最新价格
* @return
*/
public double getNewPrice(){
double newPrice= (Double) price.get(price.size()-1);
return newPrice;
}
/**
* 添加监听者
* @param listener 监听者
*/
public void addPropertyChangeListener(PropertyChangeListener listener){
this.pcs.addPropertyChangeListener(listener);
}
/**
* 通知监听者
* @param propName 给改变一个名字,好让监听者们根据这个名字来做响应
* @param oldValue 改变前的值
* @param newValue 改变后的值
*/
public void firePropertyChange(String propName, Object oldValue, Object newValue) {
this.pcs.firePropertyChange(propName, oldValue, newValue);
}
/**
* 删除监听者
* @param listener 监听者
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
this.pcs.removePropertyChangeListener(listener);
}
/**
* 打印用的方法
* @return
*/
@Override
public String toString() {
return "Stock{" +
"price=" + price +
", tendency=" + tendency +
", statisticalData=" + statisticalData +
'}';
}
}
package Observer;
public class StatisticalData {
private double max;
private double min;
private double average;
public StatisticalData(StatisticalData statisticalData) {
this.average=statisticalData.getAverage();
this.max=statisticalData.getMax();
this.min=statisticalData.getMin();
}
public StatisticalData(double max, double min, double average) {
this.max = max;
this.min = min;
this.average = average;
}
@Override
public String toString() {
return "StatisticalData{" +
"max=" + max +
", min=" + min +
", average=" + average +
'}';
}
public double getMax() {
return max;
}
public void setMax(double max) {
this.max = max;
}
public double getMin() {
return min;
}
public void setMin(double min) {
this.min = min;
}
public double getAverage() {
return average;
}
public void setAverage(double average) {
this.average = average;
}
}
package Observer;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
public class Client implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
System.out.println("变化的对象为:"+evt.getSource());
System.out.println();//空一行
System.out.println("变化的情况为:"+evt.getPropertyName());
System.out.println("变化前的内容:"+evt.getOldValue());
System.out.println("变化后的内容:"+evt.getNewValue());
System.out.println();//空一行
}
}
package demo;
import Observer.Client;
import Observer.StockMarketChange;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Demo {
public static void main(String ages[]){
StockMarketChange stockMarketChange=new StockMarketChange();
Client client=new Client();
stockMarketChange.change();
stockMarketChange.addPropertyChangeListener(client);
stockMarketChange.change();
stockMarketChange.change();
stockMarketChange.change();
stockMarketChange.change();
}
}
(5)总结:
笔者本人执行时,发现在上述代码中使用PropertyChangeSupport和PropertyChangeListener有一个问题:就是监听类无法监听到被监听类的构造函数中发生的事件。