JAVA观察者模式(observer pattern)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA观察者模式(observer pattern)相关的知识,希望对你有一定的参考价值。

必须要用观察者模式写,求提示,求代码。THANKS。
各位大神,我是初学者,能不能给的代码只用observer, 最简单的方法实现,不需要出现HashMap, 我看不太懂,谢谢了。

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;

import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class StockLists implements Runnable

    private HashMap<String, Stock>                     hmStocks = new HashMap<String, Stock>();

    private HashMap<String, ArrayList<ChangeListener>> hmCL     = new HashMap<String, ArrayList<ChangeListener>>();

    public StockLists()
    
        init(100);
        new Thread(this).start();
    

    public void run()
    
        Random rand = new Random();
        while (true)
        
            try
            
                Thread.sleep(100);
                Stock s = randStock();
                s.price = rand.nextFloat();
                ArrayList<ChangeListener> alCL = hmCL.get(s.symbol);
                if (alCL == null)
                
                    continue;
                
                for (ChangeListener cl : alCL)
                
                    cl.stateChanged(new ChangeEvent(s));
                
            
            catch (InterruptedException e)
            
                e.printStackTrace();
            
        
    

    private Stock randStock()
    
        Random rand = new Random();
        int idx = rand.nextInt(hmStocks.size());
        String sym = (String) hmStocks.keySet().toArray()[idx];
        Stock s = hmStocks.get(sym);
        return s;
    

    public boolean addChangeListener(ChangeListener cl, String sym)
    
        ArrayList<ChangeListener> al = hmCL.get(sym);
        if (al == null)
        
            al = new ArrayList<ChangeListener>();
            hmCL.put(sym, al);
        
        return al.add(cl);
    

    private void init(int count)
    
        Random rand = new Random();

        for (int i = 0; i < count; i++)
        
            String sym = randSym(rand);
            float p = rand.nextFloat();
            Stock s = new Stock(sym, p);
            hmStocks.put(sym, s);
        
    

    private String randSym(Random rand)
    
        char[] ca = new char[3];
        for (int i = 0; i < ca.length; i++)
        
            ca[i] = (char) ('A' + rand.nextInt(26));
        
        return new String(ca);
    

    public static void main(String[] args)
    
        StockLists sl = new StockLists();
        sl.addChangeListener(new Investor("111"), sl.randStock().symbol);
        sl.addChangeListener(new Investor("222"), sl.randStock().symbol);
        sl.addChangeListener(new Investor("333"), sl.randStock().symbol);
        sl.addChangeListener(new Investor("444"), sl.randStock().symbol);
        sl.addChangeListener(new Investor("555"), sl.randStock().symbol);
        sl.addChangeListener(new Investor("666"), sl.randStock().symbol);
        sl.addChangeListener(new Investor("777"), sl.randStock().symbol);
        sl.addChangeListener(new Investor("888"), sl.randStock().symbol);
        sl.addChangeListener(new Investor("999"), sl.randStock().symbol);
        sl.addChangeListener(new Investor("000"), sl.randStock().symbol);
    


class Stock

    public String symbol = null;
    public float  price  = 0.0f;

    public Stock(String sym, float p)
    
        symbol = sym;
        price = p;
    

    public String toString()
    
        return symbol + ":" + price;
    


class Investor implements ChangeListener


    public String name = "";

    public Investor(String name)
    
        this.name = name;
    

    @Override
    public void stateChanged(ChangeEvent e)
    
        Stock s = (Stock) e.getSource();
        System.out.println("Hello," + name + " : STOCK CHANGED: " + s);
    

追问

大哥,谢谢你的代码,但是你没用观察者模式啊,这个必须用观察者模式写。

追答

其实,ChangeListener/ChangeEvent就是观察者模式的一种Java实现。本质都是一样的。

追问

我是初学者,不太懂,最好能有个observer的,我好理解一点。anyway,还是谢谢你

参考技术A import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;

public class InfoCenter 

    /**
     * @param args
     */
    public static void main(String[] args) 
        // TODO Auto-generated method stub
        Random rand = new Random();
        InfoCenter infoc = new InfoCenter();
        Observer1 o1 = new Observer1();
        o1.name="sh";
        o1.stockSet.add("sgjh");
        observerSet.add(o1);

        for(int i=0;i<3;i++)
            StockChange sc = new StockChange();
            sc.put("sgjh",rand.nextFloat());
            infoc.SubjectMap.put("sgjh",sc );
            sc.change("sgjh", rand.nextFloat());
        
    
    
    public static Set<IObserver> observerSet = new HashSet<IObserver>();
    public static Map<String,StockChange<String, Float>> SubjectMap = new HashMap<String,StockChange<String, Float>>();
    public static void change(Object key, Object value)
        for(IObserver observer:observerSet)
            if(observer.contain(key))
                observer.notice(key, value);
                //SubjectMap.get(key).change(key, value);
                
            
        
    


class Stock 
    String symbol;
    Float price;

interface IObserver
    void notice(Object key, Object value);
    boolean contain(Object key);

class Observer1 implements IObserver
    public static Set<String> stockSet = new HashSet<String>();
    public String name;
    public void notice(Object key, Object value)
        if(stockSet.contains(key))
            System.out.println("name="+name+":key="+key+":"+value);
        
    @Override
    public boolean contain(Object key) 
        return stockSet.contains(key);
    

@SuppressWarnings("hiding")
class StockChange<String, Float> extends HashMap<String, Float> 

    private static final long serialVersionUID = -8239757303194985716L;
    private InfoCenter infoc;

    StockChange() 
        super();
    

    public StockChange(int initialCapacity, float loadFactor) 
        super(initialCapacity, loadFactor);
    

    public StockChange(int initialCapacity) 
        super(initialCapacity);
    

    public StockChange(Map<? extends String, ? extends Float> m) 
        super(m);
    

    public void change(String key, Float val) 
        Float v1 = get(key);
        if ((v1!=null&&val!=null&&!v1.equals(val))||!(v1==null&&val==null)) 
            InfoCenter.change(key, val);
            put(key, val);
        
    

追问

额,谢谢,不过好难,我看不太懂

追答

观察者模式主要包括三种角色,一个是观察者,一个是主题,一个是信息中心,观察者在信息中心注册了自己的名字,然后在信息中心,登记了 要关注的主题,当主题变化时,通知观察者。大概是这样吧,程序写的不工整,错漏之处在所难免,希望指正。有点仓促。

追问

唉,不好意思,我只知道用observer,observerable,我刚才学,不怎么会,你写得应该比较高级的方法,我研究研究,谢谢。

追答

我也在学习中,加油。以上程序有个BUG,就是集合的去重,需要注意一下

本回答被提问者采纳

Java_观察者模式(Observable和Observer) -转

一、观察者模式介绍

在Java中通过Observable类和Observer接口实现了观察者模式。一个Observer对象监视着一个Observable对象的变化,当Observable对象发生变化时,Observer得到通知,就可以进行相应的工作。

如果画面A是显示数据库里面的数据,而画面B修改了数据库里面的数据,那么这时候画面A就要重新Load。这时候就可以用到观察者模式

二、观察者模式实现方法

java.util.Observable中有两个方法对Observer特别重要

①setChanged()方法

    /**
     * Sets the changed flag for this {@code Observable}. After calling
     * {@code setChanged()}, {@code hasChanged()} will return {@code true}.
     */
    protected void setChanged() {
        changed = true;
    }
②notifyObservers()方法 / notifyObservers(Object data)方法

    /**
     * If {@code hasChanged()} returns {@code true}, calls the {@code update()}
     * method for every observer in the list of observers using null as the
     * argument. Afterwards, calls {@code clearChanged()}.
     * <p>
     * Equivalent to calling {@code notifyObservers(null)}.
     */
    public void notifyObservers() {
        notifyObservers(null);
    }

    /**
     * If {@code hasChanged()} returns {@code true}, calls the {@code update()}
     * method for every Observer in the list of observers using the specified
     * argument. Afterwards calls {@code clearChanged()}.
     *
     * @param data
     *            the argument passed to {@code update()}.
     */
    @SuppressWarnings("unchecked")
    public void notifyObservers(Object data) {
        int size = 0;
        Observer[] arrays = null;
        synchronized (this) {
            if (hasChanged()) {
                clearChanged();
                size = observers.size();
                arrays = new Observer[size];
                observers.toArray(arrays);
            }
        }
        if (arrays != null) {
            for (Observer observer : arrays) {
                observer.update(this, data);
            }
        }
    }

以上两个方法十分重要

setChanged()方法 ——

用来设置一个内部标志位注明数据发生了变化

notifyObservers()方法 / notifyObservers(Object data)方法 ——

通知所有的Observer数据发生了变化,这时所有的Observer会自动调用复写好的update(Observable observable, Object data)方法来做一些处理(比如说画面数据的更新)。

我们可以看到通知Observer有两个方法,一个无参,一个有参。那么这个参数有什么作用呢?

其中一个作用:现在我不想通知所有的Observer,而只想其中一个指定的Observer做一些处理,那么就可以传一个参数作为ID,然后在所有的Observer中判断,每个Observer判断只有接收到底参数ID是属于自己的才做一些处理。

当然参数还有其他作用,我只是举了个例子。


下面举个例子加以说明:

import java.util.Observable;  
/**
 * 被观察者类
 */    
public class SimpleObservable extends Observable  
{  
   private int data = 0;  
    
   public int getData(){   
       return data;  
   }  
    
   public void setData(int i){  
       if(this.data != i) { 
          this.data = i; 
          setChanged();  

          //只有在setChange()被调用后,notifyObservers()才会去调用update(),否则什么都不干。
          notifyObservers();    
       }  
   }  
}  

上面这个类是一个被观察者类,它继承了Observable类,表示这个类是可以被观察的。

然后在setData()方法里面,也就是数据改变的地方,来调用Observable类的setChanged()方法和notifyObservers()方法,表示数据已改变并通知所有的Observer调用它们的update()方法做一些处理。

注意:只有在setChange()被调用后,notifyObservers()才会去调用update(),否则什么都不干。


/**
 * 观察者类
 */      
public class SimpleObserver implements Observer  
{  
   public SimpleObserver(SimpleObservable simpleObservable){  
      simpleObservable.addObserver(this );  
   }  
    
   public void update(Observable observable ,Object data){  // data为任意对象,用于传递参数
      System.out.println(“Data has changed to” + (SimpleObservable)observable.getData());  
   }  
} 


通过生成被观察者(SimpleObservable类)的实例,来调用addObserver(this)方法让观察者(SimpleObserver类)达到观察被观察者(SimpleObservable类)的目的。

然后还要复写update()方法,做数据改变后的一些处理。

下面可以写一个简单的测试类来测试一下

public class SimpleTest  
{  
   public static void main(String[] args){  
      SimpleObservable doc = new SimpleObservable ();  
      SimpleObserver view = new SimpleObserver (doc);  
      doc.setData(1);  
      doc.setData(2);  
      doc.setData(2);  
      doc.setData(3);   
   }  
} 

运行结果如下

Data has changed to 1 
Data has changed to 2  //第二次setData(2)时由于没有setChange,所以update没被调用
Data has changed to 3

下面介绍一个Observable类的其他一些属性和方法

属性 ——

// observers是一个List,保存着所有要通知的observer。    
List<Observer> observers = new ArrayList<Observer>();
// changed是一个boolean型标志位,标志着数据是否改变了。
boolean changed = false;

方法 ——

// 添加一个Observer到列表observers中
public void addObserver(Observer observer) {
    if (observer == null) {
        throw new NullPointerException();
    }
    synchronized (this) {
        if (!observers.contains(observer))
            observers.add(observer);
    }
}

// 从列表observers中删除一个observer

public synchronized void deleteObserver(Observer observer) {
    observers.remove(observer);
}

// 清空列表observers
public synchronized void deleteObservers() {
    observers.clear();
}

// 返回列表observers中observer的个数

public int countObservers() {
    return observers.size();
}


// 重置数据改变标志位为未改变
protected void clearChanged() { 
changed = false;
}

// 将数据改变标志位设置为改变

protected void setChanged() { 
    changed = true;
}


// 判断标志位的值
public boolean hasChanged() { 
    return changed;
}

// 通知所有observer(无参)
public void notifyObservers() {
    notifyObservers(null);
}
// 通知所有observer(有参)
@SuppressWarnings("unchecked")
public void notifyObservers(Object data) { 
    int size = 0; 
    Observer[] arrays = null; 
    synchronized (this) { 
        if (hasChanged()) { 
            clearChanged(); 
            size = observers.size();
            arrays = new Observer[size]; 
            observers.toArray(arrays); 
        } 
    } 
    if (arrays != null) { 
        for (Observer observer : arrays) { 
            observer.update(this, data); 
        } 
    }
}


注意:在Observer对象销毁前一定要用deleteObserver将其从列表中删除,也就是在onDestroy()方法中调用deleteObserver()方法。

不然因为还存在对象引用的关系,Observer对象不会被垃圾收集,造成内存泄漏,并且已死的Observer仍会被通知到,有可能造成意料外的错误,而且随着列表越来越大,notifyObservers操作也会越来越慢。


下面2个工程是Observable与Observer的经典运用,是android实现的单指拖动放大图片的操作

两个例子:

http://download.csdn.net/detail/tianjf0514/4237628

http://download.csdn.net/download/tianjf0514/4237634



以上是关于JAVA观察者模式(observer pattern)的主要内容,如果未能解决你的问题,请参考以下文章

java中观察者模式Observable和Observer

利用Java提供的Observer接口和Observable类实现观察者模式

C++11:参照Java Observable实现观察者模式

Java 设计模式系列(十六)观察者模式(Observer)

java 设计模式之 观察者模式(Observer)

Java对观察者模式的支持