首页 > 代码库 > 班主任来啦之观察者模式,事件委托等Java实现---如果你还不懂,看完此文,就一定会懂
班主任来啦之观察者模式,事件委托等Java实现---如果你还不懂,看完此文,就一定会懂
班主任来啦!
小A对小B说:“今天真是笑死人了,我们班一位同学在仔细的时候看NBA球赛,被班主任抓了个正着。班主任脸都绿了,哈哈,真是笑死我了。
小B说:”啊,你们怎么同学怎么敢在课上看电视啊?“
小A说:”没有的,他们那帮子男生经常自习的时候看球赛的。我们班有个女生坐在前排,那些男生就给她送写小礼物啊什么的。班主任来了,那个女生就去通知敲一下桌子。“
小B说:”好吧。这也行。那今天怎么会有人被抓?“
小A说:”这是因为刚好班主任来的时候,那个女生去上厕所了。结果一个看漫画的男生没被抓,那个看NBA球赛的男生被抓了。手机都被没收了呢!”
小B说:“好吧。你说的这个场景,让我想起了一个设计模式,叫做观察者模式。要不给你讲讲?”
小A吐血,倒地不起。。。。。。
观察者模式
我们先来看看观察者模式的定义:观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己。在上面的场景中,观察者就是指各位苦逼同学啦,而监听的主题对象就是万恶的班主任了,班主任来的时候,通知者通知所有的同学,那么所有的同学都去自动更新自己,看NBA球赛的停下来看书,看漫画的也停下来看书。怎么样?是不是炒鸡形象,下面我们就用代码来实现上面场景里面发生的事情:
import java.util.*; //负责帮忙监听的女同学 class GirlClassMate { private String action; //前台秘书发现的情况 //给她送过礼的男同学们 private ArrayList<NbaWatcher> observers = new ArrayList<NbaWatcher>(); //增加,就是有几个同学请她帮们关照,就在集合中增加几个对象 public void Attach(NbaWatcher observer) { observers.add(observer); } //从关照对象中删除对象 public void Delete(NbaWatcher observer) { observers.remove(observer); } //通知关照看NBA的每一位同学,班主任来啦! public void Notify() { for (NbaWatcher observer : observers) { observer.Update(); } } public void setAction(String action) { this.action = action; } public String getAction() { return action; } } //看NBA的同学 class NbaWatcher { private String name; //该同学的名字 private GirlClassMate wodi; //前排卧底MM public void Update() { // TODO Auto-generated method stub System.out.println(wodi.getAction() + name + "别看球赛啦,继续学习!"); } public NbaWatcher(String name, GirlClassMate wodi) { this.name = name; this.wodi = wodi; } } public class MainClass { public static void main(String[] args) { GirlClassMate MM = new GirlClassMate(); //前台MM对象 NbaWatcher diaosi1 = new NbaWatcher("屌丝1", MM); //看股票的同事1 NbaWatcher diaosi2 = new NbaWatcher("屌丝2", MM); //看股票的同事1 //将两位屌丝同事加进前台MM的关照对象列表中 MM.Attach(diaosi1); MM.Attach(diaosi2); //前台MM发现老板回来了 MM.setAction("班主任回来啦!"); //通知关照列表中的每一位同事,老板回来了 MM.Notify(); } }输出结果:
班主任回来啦!屌丝1别看球赛啦,继续学习! 班主任回来啦!屌丝2别看球赛啦,继续学习!你来评一评,这代码写的怎么样?肯定不好啦!前排女同学对象依赖于具体的看NBA同学对象,而NBA同学对象依赖于前排女同学对象。那么这样就形成了一种双向耦合的关系。在一段代码里面,耦合度过高可不是什么好事。用依赖倒转原则专业的话来说就是:具体应该依赖于抽象,抽象不应该依赖于具体。按照这个原则,我们将看NBA球赛的观察者,看漫画的观察者等等抽象出来一个观察者类。而无论是前排MM通知者,还是班主任这个通知者都是通知者,所以也可以抽象为一个通知者类。改进之后的代码如下:
import java.util.*; //通知者,可能是班主任自己,也可能是前排女同学 abstract class Notifier { private ArrayList<Observer> observers = new ArrayList<Observer>(); public void Attach(Observer observer) { observers.add(observer); } public void Delete(Observer observer) { observers.remove(observer); } //通知关照同学列表中每一位同事,班主任来了 public void Notify() { for (Observer observer : observers) { observer.Update(); } } abstract public void setAction(String action); abstract public String getAction(); } class Boss extends Notifier{ private String action; //通知者发现的情况 //老板发现的情况 public void setAction(String action) { this.action = action; } public String getAction() { return action; } } class GirlClassMate extends Notifier{ private String action; //通知者发现的情况 //前台MM发现的情况 public void setAction(String action) { this.action = action; } public String getAction() { return action; } } //Observer师祖 abstract class Observer { protected String name; //该同学的名字 protected Notifier notifier; //事件通知者 abstract void Update(); public Observer(String name, Notifier notifier) { this.name = name; this.notifier = notifier; } } //看NBA的的同事 class NbaWatcher extends Observer{ public NbaWatcher(String name, Notifier notifier) { super(name, notifier); // TODO Auto-generated constructor stub } public void Update() { // TODO Auto-generated method stub System.out.println(notifier.getAction() + name + "别看球赛啦,继续学习!"); } } //看世界杯的同事 class ComicReader extends Observer{ public ComicReader(String name, Notifier notifier) { super(name, notifier); // TODO Auto-generated constructor stub } public void Update() { // TODO Auto-generated method stub System.out.println(notifier.getAction() + name + "别看漫画啦,继续学习"); } } public class MainClass { public static void main(String[] args) { Notifier tuhao = new Boss(); //通知者换成班主任自己了 Observer diaosi1 = new NbaWatcher("倒霉的屌丝1", tuhao); //看NBA的同学 Observer diaosi2 = new ComicReader("幸运的屌丝2", tuhao); //看NBA的同学 //将两位屌丝同事加进前台MM的关照对象列表中 //tuhao.Attach(diaosi1); //倒霉的屌丝1,没有加进老板的关照列表,所以被抓住了 tuhao.Attach(diaosi2); //前台MM发现老板回来了 tuhao.setAction("班主任我回来啦!"); //通知关照列表中的每一位同事,老板回来了 tuhao.Notify(); } }
观察者模式的不足之处
“上面该机的代码中抽象通知者还是依赖了抽象观察者,万一没有抽象观察者,那岂不是功能都完成不了啦!还有你这上面代码写的,所以对象更新的动作都一样的。万一我对象更新不一样呢?比如,看NBA球赛的听见班主任来了就跑去上厕所,而看漫画的听见班主任来了就继续看书。代码又应该怎么写呢?”小A,揉了揉惺忪的睡眼,疑惑地问道。
小B说:“我去,我还以为你睡着了呢!原来你在听啊!我太高兴了。下面我们就利用一种叫做“事件委托”的东东去解决这个问题哈!”
小A说:“我滴个神,什么叫事件委托啊?”
观察者模式改进-反射版事件委托
小B说:“你先别着急哈!我们先来看看该怎么用哈!”
代码中,我们去除了抽象观察者这个类,由客户端去决定需要通知哪个观察者。PS:Java中是没有像c#里面的delegation,所以我用Java中的反射来实现,见如下代码。
import java.util.*; import java.lang.reflect.*; //通知者,可能是老板,也可能是前台秘书 abstract class Notifier { protected Method Update; abstract public void setAction(String action); abstract public void Notify(Object object); } class Boss extends Notifier{ private String action; //通知者发现的情况 //老板发现的情况 public void setAction(String action) { this.action = action; } public void Notify(Object object) { if (Update != null) { try { Update.invoke(object, action); } catch (Exception e) { e.printStackTrace(); } } } } class GirlClassMate extends Notifier{ private String action; //通知者发现的情况 public Method Update; //反射事件 //老板发现的情况 public void setAction(String action) { this.action = action; } public void Notify(Object object) { if (Update != null) { try { Update.invoke(object, action); } catch (Exception e) { e.printStackTrace(); } } } } //看股票的同事 class NbaWatcher { protected String name; //该同事的名字 protected Notifier notifier; //前台卧底MM public NbaWatcher(String name, Notifier notifier) { this.name = name; this.notifier = notifier; } public void CloseNbaWatcher(String action) { // TODO Auto-generated method stub System.out.println(action + name + "别看球赛啦,继续学习"); } } //看世界杯的同事 class ComicReader { protected String name; //该同事的名字 protected Notifier notifier; //前台卧底MM public ComicReader(String name, Notifier notifier) { this.name = name; this.notifier = notifier; } public void CloseComicReader(String action) { // TODO Auto-generated method stub System.out.println(action + name + "别看漫画啦,继续学习"); } } public class MainClass { public static void main(String[] args) { Notifier tuhao = new Boss(); NbaWatcher diaosi1 = new NbaWatcher("倒霉的屌丝1", tuhao); ComicReader diaosi2 = new ComicReader("幸运的屌丝2", tuhao); tuhao.setAction("班主任我回来啦!"); try { tuhao.Update = diaosi1.getClass().getMethod("CloseNbaWatcher", new Class[] {String.class}); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } tuhao.Notify((Object)diaosi1); try { tuhao.Update = diaosi2.getClass().getMethod("CloseComicReader", new Class[] {String.class}); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } tuhao.Notify((Object)diaosi2); } }发射实现委托核心:
protected Method Update; if (Update != null) { try { Update.invoke(object, action); } catch (Exception e) { e.printStackTrace(); } } try { tuhao.Update = diaosi2.getClass().getMethod("CloseComicReader", new Class[] {String.class}); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } tuhao.Notify((Object)diaosi2);右上面代码可以看出,反射能够实现根据类的不同而调用不同的方法。就好像是C#中,一个委托可以搭载多个方法,所有方法一次被唤起。感觉上,也有点像c语言里面的钩子函数。
后继
小A说:哇!小B你好厉害哦,我真的懂了观察模式诶,另外发射模式真的好好用啊!
小B说:O(∩_∩)O哈哈~你学到东西就好!其实该感谢本文作者的吐血整理,我,笑了~~~~~~~你们都懂了就好!
班主任来啦之观察者模式,事件委托等Java实现---如果你还不懂,看完此文,就一定会懂
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。