观察者模式(九)
观察者模式
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。
介绍
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
如何解决:使用面向对象技术,可以将这种依赖关系弱化。
关键代码:在抽象类里有一个 ArrayList 存放观察者们。
应用实例: 1、拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。 2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。
优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
使用场景:
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
- 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
- 一个对象必须通知其他对象,而并不知道这些对象是谁。
- 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
注意事项: 1、JAVA 中已经有了对观察者模式的支持类。 2、避免循环引用。 3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
package com.tzy.Observer.com.old; import java.util.Observable; /** * @author Heaton * @date 2018/5/7 22:57 * @describe 被观察人 注意set方法 */ public class Person extends Observable { private String name; private String sex; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; System.out.println("改名字"); this.setChanged();//说明对象已经改变 this.notifyObservers();//当对象改变时通知观察者,被观察者改变了 } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; System.out.println("改性别"); this.setChanged(); this.notifyObservers(); } public int getAge() { return age; } public void setAge(int age) { this.setChanged(); this.notifyObservers(); this.age = age; System.out.println("改年龄"); } }
package com.tzy.Observer.com.old; import java.util.Observable; import java.util.Observer; /** * @author Heaton * @date 2018/5/7 22:56 * @describe 观察者 */ public class MyObServer implements Observer { public void update(Observable o, Object arg) { System.out.println("对象发生变化"); } }
package com.tzy.Observer.com.old; public class MainClass { public static void main(String[] args) { Person person = new Person(); //一定要注册,谁来观察 person.addObserver(new MyObServer()); person.addObserver(new MyObServer()); person.addObserver(new MyObServer()); System.out.println("观察者数量:"+person.countObservers()); person.setName("lifengxing"); person.setAge(23); person.setSex("男"); } }
注意看 改年龄的set方法,发现只要提示对象改变,那么就会通知观察者执行。继续玩
package com.tzy.Observer.com.news; /** * @author Heaton * @date 2018/5/7 23:10 * @describe 文章 */ public class Article { private String articleTitle; private String articleContent; public String getArticleTitle() { return articleTitle; } public void setArticleTitle(String articleTitle) { this.articleTitle = articleTitle; } public String getArticleContent() { return articleContent; } public void setArticleContent(String articleContent) { this.articleContent = articleContent; } }
package com.tzy.Observer.com.news; import java.util.Observable; /** * @author Heaton * @date 2018/5/7 23:10 * @describe 博主 被观察者 */ public class BlogUser extends Observable { public void publishBlog(String articleTitle,String articleContent) { Article art = new Article(); art.setArticleTitle(articleTitle); art.setArticleContent(articleContent); System.out.println("Heaton现在发表新****文章标题:" + articleTitle + ",文章内容:" + articleContent); this.setChanged(); this.notifyObservers(art); } }
package com.tzy.Observer.com.news; import java.util.Observable; import java.util.Observer; /** * @author Heaton * @date 2018/5/7 23:17 * @describe 订阅者 */ public class MyObServer implements Observer { public void update(Observable o, Object arg) { Article art = (Article)arg; System.out.println("博主发表新文章了!"); System.out.println("文章标题" + art.getArticleTitle()); System.out.println("文章内容:" + art.getArticleContent()); } }
package com.tzy.Observer.com.news; public class MainClass { public static void main(String[] args) { BlogUser user = new BlogUser(); user.addObserver(new MyObServer()); user.publishBlog("我爱你", "爱你爱你就爱你"); } }
**********************demo 天气预报
package com.tzy.Observer.com.demo; /** * @author Heaton * @date 2018/5/10 21:45 * @describe 公告板 */ public class CurrentConditions { private float mTemperature; private float mPressure; private float mHumidity; public void update(float mTemperature,float mPressure,float mHumidity) { this.mTemperature=mTemperature; this.mPressure=mPressure; this.mHumidity=mHumidity; display(); } public void display() { System.out.println("***Today mTemperature: "+mTemperature+"***"); System.out.println("***Today mPressure: "+mPressure+"***"); System.out.println("***Today mHumidity: "+mHumidity+"***"); } }
package com.tzy.Observer.com.demo; /** * @author Heaton * @date 2018/5/10 21:46 * @describe 天气预告 */ public class WeatherData { private float mTemperatrue; private float mPressure; private float mHumidity; private CurrentConditions mCurrentConditions; public WeatherData(CurrentConditions mCurrentConditions) { this. mCurrentConditions= mCurrentConditions; } public float getTemperature() { return mTemperatrue; } public float getPressure() { return mPressure; } public float getHumidity() { return mHumidity; } public void dataChange() { mCurrentConditions.update(getTemperature(), getPressure(), getHumidity()); } public void setData(float mTemperature,float mPressure,float mHumidity) { this.mTemperatrue=mTemperature; this.mPressure=mPressure; this.mHumidity=mHumidity; dataChange(); } }
package com.tzy.Observer.com.demo; public class InternetWeather { public static void main(String[] args) { CurrentConditions mCurrentConditions; WeatherData mWeatherData; mCurrentConditions=new CurrentConditions(); mWeatherData=new WeatherData(mCurrentConditions); mWeatherData.setData(30, 150, 40); } }
好了,问题来了,新需求,某某公司说 我们也要一个公告板,那么天气遇到就要通知他,所以就在datachange方法里加入一个新的公告板。
但是这样,天气预报这个接口肯定是一直在运行的,不可能停了来增加新的公告板,所以这样不科学,要做到动态添加。
package com.tzy.Observer.com.demo.observer; public interface Subject { public void registerObserver(Observer o); public void removeObserver(Observer o); public void notifyObservers(); }
package com.tzy.Observer.com.demo.observer; public interface Observer { public void update(float mTemperatrue, float mPressure, float mHumidity); }
package com.tzy.Observer.com.demo.mode; import com.tzy.Observer.com.demo.observer.Observer; import com.tzy.Observer.com.demo.observer.Subject; import java.util.ArrayList; /** * @author Heaton * @date 2018/5/10 22:05 * @describe 天气预报,被观察者,一旦天气变化通知所有观察者 */ 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()); } } }
package com.tzy.Observer.com.demo.mode; import com.tzy.Observer.com.demo.observer.Observer; /** * @author Heaton * @date 2018/5/10 22:03 * @describe 新的一个中文公告板,用来接收天气预报的变化 */ 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())+"**"); } }
package com.tzy.Observer.com.demo.mode; import com.tzy.Observer.com.demo.observer.Observer; /** * @author Heaton * @date 2018/5/10 22:02 * @describe 英文公告板 观察者,接受被观察者天气预报的变化 */ 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 + "***"); } }
package com.tzy.Observer.com.demo.mode; 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); System.out.println("*******去掉一个观察者**********"); mWeatherDataSt.setData(40, 250, 50); } }
再用内置观察者走一波,注意观察者是有推----拉 两种情况,数据大就拉,数据小就推,根据业务需要。(注意java内置观察者是后注册的先通知)
notifyObservers(),这个被观察者的方法,有无参的和有参的2种,有参的就是推数据,数据及参数;无参的就是采用拉的方式,只是通知你有变化。
package com.tzy.Observer.com.demo.jv; import java.util.Observable; 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();//通知可控,比如0.1秒就变化了,而我们要0.5秒一次的数据,那么这个函数的必要性就体现了 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; } } }
package com.tzy.Observer.com.demo.jv; import java.util.Observable; import java.util.Observer; 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=((WeatherData.Data)(arg1)).mTemperatrue; this.mPressure=((WeatherData.Data)(arg1)).mPressure; this.mHumidity=((WeatherData.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+"***"); } }
package com.tzy.Observer.com.demo.jv; import java.util.Observable; import java.util.Observer; 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=((WeatherData.Data)(arg1)).mTemperatrue; this.mPressure=((WeatherData.Data)(arg1)).mPressure; this.mHumidity=((WeatherData.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)+"***"); } }
package com.tzy.Observer.com.demo.jv; 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); System.out.println("*******去掉一个观察者**********"); mWeatherData.deleteObserver(mCurrentConditions); mWeatherData.setData(35, 150, 60); } }
总结一下:
观察者模式什么时候用:对象之间多对一依赖的一种设计方案,被依赖的对象Observable(Subject)也就是被观察者,依赖的对象为Observer也就是观察者,被观察者通知观察者变化。
通俗易懂,送牛奶,送报纸,只要是送什么什么,观察者都是首选
您的资助是我最大的动力!
金额随意,欢迎来赏!