觀察者模式原理
Internet氣象站項(xiàng)目富雅,普通的OO設(shè)計(jì)方案有問(wèn)題掸驱。
- Internet氣象站項(xiàng)目:
- 提供溫度、氣壓和濕度的接口
- 測(cè)量數(shù)據(jù)更新時(shí)需試試通知給第三方
- 需要設(shè)計(jì)開(kāi)放型API没佑,便于其他第三方公司也能接入氣象站獲取數(shù)據(jù)
- WeatherData類(lèi)
在第三方獲取數(shù)據(jù)的時(shí)候毕贼,datachange()函數(shù)里就要去通知相應(yīng)的類(lèi),造成一個(gè)代碼的重復(fù)蛤奢。那么在這里的一個(gè)解決的辦法就是將被通知的類(lèi)抽象鬼癣,在氣象站類(lèi)里進(jìn)行注冊(cè),氣象站會(huì)向在列表中的對(duì)象發(fā)放通知提醒啤贩,這樣氣象站就不需要重新編譯停下來(lái)待秃。
原理
- 觀察者模式就像定牛奶
- 奶站 subject
- 用戶 observer
- Subject:登記注冊(cè)、移除和通知
- Observer:接收輸入
- 觀察者模式:對(duì)象之間多對(duì)一依賴的一種設(shè)計(jì)方案痹屹,被依賴的對(duì)象為Subject章郁,依賴的對(duì)象為Observer,Subject通知Observer變化痢掠。
用觀察者重新設(shè)計(jì)的方案
首先定義subject接口驱犹,用于注冊(cè)刪除通知其他觀察者
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}
然后定義Observer接口,用于接收更新的數(shù)據(jù)
public interface Observer {
public void update(float mTemperatrue,float mPressure,float mHumidity);
}
定義subject的氣象站實(shí)現(xiàn)類(lèi) 用于發(fā)送數(shù)據(jù) 注冊(cè)觀察者
在這里用了一個(gè)ArrayList數(shù)組來(lái)保存觀察者足画,相當(dāng)于一個(gè)注冊(cè)表的功能
public class WeatherDataSt implements Subject{
private float mTemperatrue;
private float mPressure;
private float mHumidity;
private ArrayList<Observer> mObservers;
public WeatherDataSt()
{
mObservers=new ArrayList<Observer>();
}
public float getTemperature()
{
return mTemperatrue;
}
public float getPressure()
{
return mPressure;
}
public float getHumidity()
{
return mHumidity;
}
public void dataChange()
{
notifyObservers();
}
public void setData(float mTemperatrue,float mPressure,float mHumidity)
{
this.mTemperatrue=mTemperatrue;
this.mPressure=mPressure;
this.mHumidity=mHumidity;
dataChange();
}
@Override
public void registerObserver(Observer o) {
// TODO Auto-generated method stub
mObservers.add(o);
}
@Override
public void removeObserver(Observer o) {
// TODO Auto-generated method stub
if(mObservers.contains(o))
{mObservers.remove(o);}
}
@Override
public void notifyObservers() {
// TODO Auto-generated method stub
for(int i=0,len=mObservers.size();i<len;i++)
{
mObservers.get(i).update(getTemperature(), getPressure(), getHumidity());
}
}
}
定義兩個(gè)觀察者實(shí)現(xiàn)類(lèi)雄驹,update用于接收數(shù)據(jù),display用于打印
public class ForcastConditions implements Observer{
private float mTemperatrue;
private float mPressure;
private float mHumidity;
@Override
public void update(float mTemperatrue, float mPressure, float mHumidity) {
// TODO Auto-generated method stub
this.mTemperatrue=mTemperatrue;
this.mPressure=mPressure;
this.mHumidity=mHumidity;
display();
}
public void display()
{
System.out.println("**明天溫度:"+(mTemperatrue+Math.random())+"**");
System.out.println("**明天氣壓:"+(mPressure+10*Math.random())+"**");
System.out.println("**明天濕度:"+(mHumidity+Math.random())+"**");
}
}
public class CurrentConditions implements Observer {
private float mTemperatrue;
private float mPressure;
private float mHumidity;
@Override
public void update(float mTemperatrue, float mPressure, float mHumidity) {
// TODO Auto-generated method stub
this.mHumidity = mHumidity;
this.mPressure = mPressure;
this.mTemperatrue = mTemperatrue;
display();
}
public void display() {
System.out.println("***Today mTemperatrue:" + mTemperatrue + "***");
System.out.println("***Today mPressure:" + mPressure + "***");
System.out.println("***Today mHumidity:" + mHumidity + "***");
}
}
下民是一個(gè)測(cè)試類(lèi)
public class InternetWeather {
public static void main(String[] args) {
CurrentConditions mCurrentConditions;
ForcastConditions mForcastConditions;
WeatherDataSt mWeatherDataSt;
mWeatherDataSt=new WeatherDataSt();
mCurrentConditions=new CurrentConditions();
mForcastConditions=new ForcastConditions();
mWeatherDataSt.registerObserver(mCurrentConditions);
mWeatherDataSt.registerObserver(mForcastConditions);
mWeatherDataSt.setData(30, 150, 40);
mWeatherDataSt.removeObserver(mCurrentConditions);
mWeatherDataSt.setData(40, 250, 50);
}
}
其實(shí)觀察者模式的想法就是基于插銷(xiāo)原理淹辞,實(shí)現(xiàn)一個(gè)主類(lèi)一直運(yùn)行医舆,然后獲取數(shù)據(jù)的接口類(lèi)一個(gè)可插拔解耦。
Java中的觀察者模式
Java中其實(shí)也有已經(jīng)實(shí)現(xiàn)的觀察者的類(lèi)象缀,那么subject類(lèi)集成Observable類(lèi)蔬将,變成一個(gè)主站,其他兩個(gè)接收數(shù)據(jù)的子類(lèi)實(shí)現(xiàn)Observer接口央星,成為一個(gè)觀察者霞怀,主站可以調(diào)用nofityObservers方法來(lái)通知觀察者。那么觀察者這里接收數(shù)據(jù)莉给,java給了兩個(gè)選擇毙石,一種是主站推數(shù)據(jù),一種是觀察者拉數(shù)據(jù)颓遏,選擇自己需要的數(shù)據(jù)進(jìn)行獲取徐矩。
下面貼出示例代碼 不做解釋
public class WeatherData extends Observable{
private float mTemperatrue;
private float mPressure;
private float mHumidity;
public float getTemperature()
{
return mTemperatrue;
}
public float getPressure()
{
return mPressure;
}
public float getHumidity()
{
return mHumidity;
}
public void dataChange()
{
this.setChanged();
this.notifyObservers(new Data(getTemperature(),getPressure(),getHumidity()));
}
public void setData(float mTemperatrue,float mPressure,float mHumidity)
{
this.mTemperatrue=mTemperatrue;
this.mPressure=mPressure;
this.mHumidity=mHumidity;
dataChange();
}
public class Data
{
public float mTemperatrue;
public float mPressure;
public float mHumidity;
public Data(float mTemperatrue,float mPressure,float mHumidity)
{
this.mTemperatrue=mTemperatrue;
this.mPressure=mPressure;
this.mHumidity=mHumidity;
}
}
}
public class ForcastConditions implements Observer {
private float mTemperatrue;
private float mPressure;
private float mHumidity;
@Override
public void update(Observable arg0, Object arg1) {
// TODO Auto-generated method stub
this.mTemperatrue=((Data)(arg1)).mTemperatrue;
this.mPressure=((Data)(arg1)).mPressure;
this.mHumidity=((Data)(arg1)).mHumidity;
display();
}
public void display()
{
System.out.println("***Tomorrow mTemperatrue:" +(mTemperatrue+1)+"***");
System.out.println("***Tomorrow mPressure:" +(mPressure+1)+"***");
System.out.println("***Tomorrow mHumidity:" +(mHumidity+1)+"***");
}
}
public class ForcastConditions implements Observer {
private float mTemperatrue;
private float mPressure;
private float mHumidity;
@Override
public void update(Observable arg0, Object arg1) {
// TODO Auto-generated method stub
this.mTemperatrue=((Data)(arg1)).mTemperatrue;
this.mPressure=((Data)(arg1)).mPressure;
this.mHumidity=((Data)(arg1)).mHumidity;
display();
}
public void display()
{
System.out.println("***Tomorrow mTemperatrue:" +(mTemperatrue+1)+"***");
System.out.println("***Tomorrow mPressure:" +(mPressure+1)+"***");
System.out.println("***Tomorrow mHumidity:" +(mHumidity+1)+"***");
}
}
public class CurrentConditions implements Observer {
private float mTemperatrue;
private float mPressure;
private float mHumidity;
@Override
public void update(Observable arg0, Object arg1) {
// TODO Auto-generated method stub
this.mTemperatrue=((Data)(arg1)).mTemperatrue;
this.mPressure=((Data)(arg1)).mPressure;
this.mHumidity=((Data)(arg1)).mHumidity;
display();
}
public void display()
{
System.out.println("***Today mTemperatrue:" +mTemperatrue+"***");
System.out.println("***Today mPressure:" +mPressure+"***");
System.out.println("***Today mHumidity:" +mHumidity+"***");
}
}
public class InternetWeather {
public static void main(String[] args) {
CurrentConditions mCurrentConditions;
ForcastConditions mForcastConditions;
WeatherData mWeatherData;
mCurrentConditions=new CurrentConditions();
mForcastConditions=new ForcastConditions();
mWeatherData=new WeatherData();
mWeatherData.addObserver(mCurrentConditions);
mWeatherData.addObserver(mForcastConditions);
mWeatherData.setData(30, 150, 40);
mWeatherData.deleteObserver(mCurrentConditions);
mWeatherData.setData(35, 150, 60);
}
}
觀察者模式關(guān)鍵點(diǎn)
- 松耦合 高內(nèi)聚 隔離影響
- java內(nèi)置觀察者使用注意點(diǎn)。Observable是一個(gè)類(lèi)叁幢,只能繼承滤灯,而java又是單繼承的,所以要注意這一點(diǎn)。