JavaBean PropertyChange 之设计模式Observer

JavaBean PropertyChange 之设计模式Observer(转)

java语言里包含了许多对设计模式的直接支持,如command模式,agent模式,observer模式等。虽然java提供的对这些模式的支持很简单,不能满足比较复杂的应用。但在简单的场景下,使用这些类往往能够得到立杆见影的效果。所以,如果没有什么特殊需求,还是最好利用java的这些类。

        Observer模式,又称监听模式,观察者模式,是经典设计模式之一(one of GOF)。java语言中,对这种模式支持的类和接口主要有以下几个,全部来自java.beans包:

java.beans.PropertyChangeListener (interface)  

java.beans.PropertyChangeSupport  (class)

java.beans.PropertyChangeEvent (class)

  • java.beans.PropertyChangeListener

      这是一个接口,很显然,所有实现这个接口的类就是listener啦(或者叫observer),它会对被监听的对象的某些变化感兴趣。这个接口就一个方法:

java 代码
  1. public void propertyChange(PropertyChangeEvent evt) {   
  2. // TODO Auto-generated method stub   
  3. }  

接口定义很简单,作用也很明显。接受一个event(被监听者产生的PropertyChangeEvent),然后根据这个event做点反应。

  •   java.beans.PropertyChangeSupport 

         这个类用在被观察者的类里,用来保存注册的观察者,并负责向他们提供被观察者的变化信息。这个类的方法也不多,不过还是只介绍100%用到的,要不脑子就不够使了,呵呵。

java 代码
  1. public PropertyChangeSupport(Object sourceBean)  

      这是构造函数,参数就是被监听者。PropertyChangeListener一般作为被监听者的一个属性。一般如下使用:

java 代码
  1. private PropertyChangeSupport listeners = new PropertyChangeSupport(this);  

      注意,这个listeners可不是只代表一个监听者,他可能是一群监听者。那么如何这些listeners是谁呢?这回用到下面的方法了。

java 代码
  1. public void addPropertyChangeListener(PropertyChangeListener listener)  

        这个类太容易了,把监听者加进来。就像开十七大一样,记者想要采访,就得先登记一下。显然这个方法可以多次调用(add嘛)。有加就有减:

java 代码
  1. public void removePropertyChangeListener(PropertyChangeListener listener)  

如果这个监听者对被监听者的任何变化多不感兴趣了,就被被监听者赶了出去。

好了,记者都到齐了,被监听者有变化了就该通知人家了,使用如下方法的一个:

java 代码
  1. public void firePropertyChange(PropertyChangeEvent evt)   
  2.   
  3. public void firePropertyChange(String propertyName,   
  4.                                boolean oldValue,   
  5.                                boolean newValue)   
  6.   
  7. public void firePropertyChange(String propertyName,   
  8.                                int oldValue,   
  9.                                int newValue)   
  10.   
  11. public void firePropertyChange(String propertyName,   
  12.                                Object oldValue,   
  13.                                Object newValue)  

实际上,后三个方法的参数都会封装成PropertyChangeEvent,然后调用第一个方法。不过在实际中,我们还是喜欢直接调用后三个中的一个,封装的事我们就不管了。后三个方法的参数都是三个,其中的oldValue和 newValue就是改变前后的值,第一个就是给改变一个名字,好让监听者们根据这个名子来做响应。就像开会,政府的所有信息都会被记者听到,但是有的记者只对台湾问题感兴趣,而有的记者对中日问题感兴趣。

对PropertyChangeSupport方法的介绍就这么多吧。注意,PropertyChangeSupport既然被用到了被观察者的类(一般是一个model)里,那么他的这些方法就只在被观察这里调用。

  • java.beans.PropertyChangeEvent

       这个类我也懒得介绍,看看他的主要方法就明白怎么回事了

java 代码
  1. public String getPropertyName()   
  2. public Object getNewValue()   
  3. public Object getOldValue()  

就者三个类,再有就是具体问题具体分析了。来个例子吧,首先是被观察者:

java 代码
  1. public class Domain{   
  2.     protected String id;   
  3.     protected String name;   
  4.     protected String desName;   
  5.   
  6.     protected PropertyChangeSupport listeners = new PropertyChangeSupport(this);   
  7.   
  8.     public String getId() {   
  9.         return id;   
  10.     }   
  11.   
  12.     public void setId(String id) {   
  13.         this.id = id;   
  14.         firePropertyChange("Domain.id"null, id);   
  15.     }   
  16.   
  17.     public String getDesName() {   
  18.         return desName;   
  19.     }   
  20.   
  21.     public void setDesName(String desName) {   
  22.         this.desName = desName;   
  23.         firePropertyChange("Domain.desName"null, desName);   
  24.     }   
  25.   
  26.     public String getName() {   
  27.         return name;   
  28.     }   
  29.   
  30.     public void setName(String name) {   
  31.         this.name = name;   
  32.         firePropertyChange("Domain.name"null, name);   
  33.     }   
  34.   
  35.     public void addPropertyChangeListener(PropertyChangeListener listener) {   
  36.         listeners.addPropertyChangeListener(listener);   
  37.     }   
  38.   
  39.     public void firePropertyChange(String propName, Object oldValue, Object newValue) {   
  40.         listeners.firePropertyChange(propName, oldValue, newValue);   
  41.     }   
  42.   
  43.     public void removePropertyChangeListener(PropertyChangeListener listener) {   
  44.         listeners.removePropertyChangeListener(listener);   
  45.     }   
  46. }  

有人对Domain的三个属性感兴趣。下面就是这些人中的一个:

java 代码
  1. public class SimpleObserver implements PropertyChangeListener {   
  2.        
  3.     ....   
  4.        
  5.     @Override  
  6.     public void propertyChange(PropertyChangeEvent evt) {   
  7.         if(evt.getPropertyName().equals("Domain.name")){   
  8.             //do some work   
  9.         }   
  10.     }   
  11.        
  12. }  

下面是个简单的测试类:

java 代码
  1. public class SimpleTest{   
  2.     public static void main(String[] args) {   
  3.         SimpleObserver observer = new SimpleObserver();   
  4.         Domain domain = new Domain();   
  5.         domain.addPropertyChangeListener(observer);   
  6.         domain.setName("yangsq");   
  7.         ......   
  8.     }   
  9. }  
关于JavaBean的PropertyChangeListener

JavaBean的属性与一般Java程序中所指的属性,或者说与所有面向对象的程序设计语言中对象的属性是一个概念,在程序中的具体体现就是类中的变量。在JavaBean的设计中,按照属性的不同作用又细分为四类:单值属性;索引属性;关联属性;限制属性。

  本文主要介绍如何使用PropertyChangeSupport类来支持关联属性事件的触发。

  1.关联属性

  关联属性,也称之为绑定属性。绑定属性会在属性值发生变化时,通知所有相关的监听器。为了实现一个绑定属性,必须实现两个机制。

  1) 无论何时,只要属性的值发生变化,该bean必须发送一个PropertyChange事件给所有已注册的监听器。该变化可能发生在调用set方法时,或者程序的用户做出某种动作时。

  2) 为了使感兴趣的监听器能够进行注册,bean必须实现以下两个方法:

void addPropertyChangeListener(PropertyChangeListener listener);
void removePropertyChangeListener(PropertyChangeListener listener);

  2.使用PropertyChangeSupport 管理 监听器

  可以通过java.bean包下的PropertyChangeSupport类来管理监听器。要使用这个类,bean必须有一个此类的数据域。

private PropertyChangeSupport changes = new PropertyChangeSupport(this);

  这样就可以将添加和移除监听器的任务交给这个对象。

public void addPropertyChangeListener(PropertyChangeListener listener) {
changes.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
changes.removePropertyChangeListener(listener);
}

当bean的属性发生变化时,使用PropertyChangeSupport对象的firePropertyChange方法,它会将一个事件发送给所有已经注册的监听器。该方法有三个参数:属性的名字、旧的值以及新的值。属性的值必须是对象,如果是简单数据类型,则必须进行包装。

changes.firePropertyChange("ourString", oldString, newString);

  所有注册的监听器实现PropertyChangeListener接口,该接口中只有一个方法。

public void propertyChange(PropertyChangeEvent e);

  当bean的属性值发生变化时,该方法中的代码就会被触发。可以通过

  e.getOldValue();

  e.getNewValue();

  来得到changes.firePropertyChange("ourString", oldString, newString);中的oldString和newString。

  3.为什么要使用PropertyChangeSupport

  使用这个类 管理 监听器的好处是,它是线程安全的。如果使用一个循环体来set Bean的属性,则这个类可以保证所有监听器执行触发事件的有序。

  还有一个好处是,这个类支持fire带索引的属性改变事件(详见java.bean.IndexedPropertyChangeEvent)。此时向注册的监听器发送一个PropertyChangeEvent的方法为:

void fireIndexedPropertyChange(String PropertyName,int index,Object oldValue,Object newValue);

  4.示例

  MyBoundBean类(具体代码见附件)是一个JavaBean,我们关注它的唯一一个属性ourString的变化情况,它的初始值是Hello。并通过PropertyChange类来管理监听器。注意在set方法中会调用firePropertyChange方法。

MyBoundBean.java
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
public class MyBoundBean {
String ourString = "Hello";
private PropertyChangeSupport changes = new PropertyChangeSupport(this);
public void setString(String newString) {
String oldString = ourString;
ourString = newString;
changes.firePropertyChange("ourString", oldString, newString);
}
public String getString() {
return ourString;
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
changes.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
changes.removePropertyChangeListener(listener);
}
}

 

MyBoundBean b = new MyBoundBean();

public void actionPerformed(ActionEvent e) {
jButton1.setText(textBox.getText());
b.setString(textBox.getText());
}

  目标bean的属性一改变(注意,初始值是"Hello"),将会触发propertyChange方法的执行。将文本框的内容设置为目标bean的ourString属性的旧值,同时,将jButton2的test设置成目标bean的ourString属性的新值。

public void propertyChange(PropertyChangeEvent e) {
if (e.getSource() == b) {
textBox.setText(e.getOldValue().toString());
jButton2.setText(e.getNewValue().toString());
}
}

  如果不实现PropertyChangeListener接口的话,可以使用匿名内部类来达到同样的效果。(具体代码见附件MyCallBound2.java)

MyBoundBean b = new MyBoundBean();

b.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
// 这样一来,我们就可以用自己定义的名字实现事件
ourString_propertyChange(e);
}
});

 

  这样一来,我们就可以用自己定义的名字实现事件。

void ourString_propertyChange(PropertyChangeEvent e) {
if (e.getSource() == b) {
textBox.setText(e.getOldValue().toString());
jButton2.setText(e.getNewValue().toString());
}
}

很显然,可以观察到SimpleObserver中propertyChange方法的执行。



你可能感兴趣的:(JavaBean PropertyChange 之设计模式Observer)