老李:小张啊,最近忙嘛呢?下班就跑 关于观察者模式的定义,我就直接引用HeadFirst书中的描述了:观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。我们通常把有状态的对象称为主题,收到主题通知的依赖者对象称为观察者。主题和观察者定义了一对多的关系,观察者依赖于主题,只要主题状态一有变化,观察者就会被通知。类图见下: 我们来用程序语音来描述下,以彩票官网为例,彩民可以自由的向其注册或取消注册,当中奖号码更新后,即官网此状态改变后,每个注册过的彩民都会收到官网传来的通知。这里的官网就相当于我们所说的主题,彩民相当于我们的观察者,我们可先创建一个主题接口类: 为什么要使用接口,而不是直接使用具体的主题类,因为不想主题与观察者过分耦合,要努力使对象之间的互相依赖降到最低,这样才能够应付变化,建立有弹性的OO系统。 这是一个彩民接口即观察者接口,这个接口只有一个updateLottery(Lottery lottery)方法,当主题的状态改变时它就会被调用。 这是一个具体的主题类,一个具体的主题总是实现主题接口,除了注册和取消注册方法之外,具体主题还实现了notifyLottery()方法,此方法用于在状态改变时更新所有当前观察者,即彩票信息改变时,将彩票的当前信息通知给彩民。 当彩票状态改变时,我们将Lottery数据直接推(push)给了观察者,但是有的观察者可能只需要一点点数据(如只需要获奖数字不需要时间),并不想被强迫的收到所有数据。这时我们可以考虑让观察者自己从主题中拉(pull)数据,主题只需要提供公开的get方法即可。这是彩票的实体类,包括彩票的所属日期和当前中奖号码,可以根据需要随意增添。 这是具体的观察者彩民1号,观察者必须实现IObserver接口和注册具体主题,以便接收更新。 根据需要我们可以随意添加观察者,因为观察者和主题之间是松耦合的,所以我们改变观察者或者主题其中一方,并不会影响另一方。我们来测试一下这个设计吧。 除了我们自己实现一整套观察者模式,java还提供了内置的观察者模式。java.util包(package)内包含最基本的Observer接口和Observable类,这和我们的Observer接口和Subject接口很相似。同样的场景我们用内置观察者模式看下: 这是一个具体的主题类,因为Observable是个具体类而不是接口,所以在扩展性上不是很灵活,限制了Observable的复用潜力。 Observable为我们提供了notifyObservers()方法和notifyObservers(Object arg)方法,所以如果你想推(push)数据给观察者,直接可以把数据对象传递给一个参数的更新方法,而如果你想让观察者拉(pull)数据,只需要调用无参数更新方法,同时提供公开的get方法即可。这是具体的观察者彩民1号 来测试一下这个设计吧。需要注意的是,内置的观察者模式,通知的次序不同于我们注册的次序,所以当我们对于通知顺序有要求的时候,不能使用内置的观察者模式。
小张:昨天买了彩票,今天去看下自己是否财务自由了
老李:官网注册个账号,坐等中奖号码通知不香吗?
小张:我就是不想加班…,嗯确实香,老李你先把刀放下
老李:哪天心灰意冷了,就注销掉,别辜负了工作给你的热情
小张:…观察者模式
/** * 这是主题类。 * * <p>用户只要向其注册,主题状态改变后, * 就可以收到官网发送来的彩票信息。 */ public interface ISubject { // 给彩民用户提供的注册和移除方法 void registerObserver(IObserver o); void removeObserver(IObserver o); // 给用户发送“彩票信息变化通知” void notifyLottery(); }
/** * 观察者接口类。 * * <p>所有的观察者都必须实现该接口,关于观察者的一切 * 主题只知道观察者实现了当前接口即IObserver * 主题不需要知道观察者的具体类是谁、做了些什么或其他任何细节 * 这就使主题和观察者之间的依赖程度非常低。 */ public interface IObserver { // 当知道彩票信息更新后的处理方法 void updateLottery(Lottery lottery); }
/** * 具体的主题类 */ public class LotteryData implements ISubject{ // 持有彩民(观察者)的类 private ArrayList<IObserver> list = new ArrayList<>(); // 彩票信息类 private Lottery lottery; @Override public void registerObserver(IObserver o) { list.add(o); } @Override public void removeObserver(IObserver o) { int index = list.indexOf(o); if(index != -1){ list.remove(index); } } @Override public void notifyLottery() { for(IObserver o : list){ o.updateLottery(lottery); } } /** * 智能彩票机开始摇号。 * * <p>这里模拟5s为1天的情况,每5s彩票状态改变一次。 */ public void beginWork() { Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { notifyInfo(); } }, 0, 5000); } private void notifyInfo() { if(lottery == null) lottery = new Lottery(); // 添加日期 lottery.setDate(new Date()); // 添加中奖数字,这里测试只有五位数了 lottery.setWinningCount(new Random().nextInt(90000)+10000); // 彩票状态改变,通知自己的所有依赖者进行更新 notifyLottery(); } }
/** * 彩票信息类 */ public class Lottery { // 彩票的日期 private Date date; // 彩票的获奖数字 private int winningCount; public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public int getWinningCount() { return winningCount; } public void setWinningCount(int winningCount) { this.winningCount = winningCount; } }
/** * 具体的观察者 */ public class LotteryBuyerOne implements IObserver{ public LotteryBuyerOne(ISubject s) { // 注册 s.registerObserver(this); } @Override public void updateLottery(Lottery lottery) { System.out.println("我是彩民1号 彩票日期:"+lottery.getDate()+" 中奖号码为:"+lottery.getWinningCount()); } }
public class ObserverPatternTest { public static void main(String[] args) { // 声明一个主题 final LotteryData subject = new LotteryData(); // 注册彩民用户 final LotteryBuyerOne loOne = new LotteryBuyerOne(subject); subject.beginWork(); final Timer timer = new Timer(); // 6s后彩民1号,因为总中不了奖失去了兴趣,取消注册了 timer.schedule(new TimerTask() { @Override public void run() { subject.removeObserver(loOne); timer.cancel(); } }, 6000); } }
三、内置观察者模式
/** * 具体的主题 */ public class LotteryData extends Observable{ // 彩票信息类 private Lottery lottery; /** * 这里模拟每5s彩票状态改变一次 */ public void beginWork() { Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { notifyInfo(); } }, 0, 5000); } public void notifyInfo() { if(lottery == null) lottery = new Lottery(); // 添加日期 lottery.setDate(new Date()); // 添加中奖数字,这里测试只有五位数了 lottery.setWinningCount(new Random().nextInt(90000)+10000); // 彩票状态改变,通知自己的所有依赖者进行更新 updata(); } /** * 提供了 */ public Date getDate() { return lottery.getDate(); } public int getWinningCount() { return lottery.getWinningCount(); } private void updata() { setChanged(); // 改变状态 notifyObservers(this); // 通知观察者 } }
/** * 具体的观察者1号。 * 通过向官网注册,当彩票状态发生改变获得通知。 */ public class LotteryBuyerOne implements Observer{ public LotteryBuyerOne(Observable observable) { observable.addObserver(this); } @Override public void update(Observable o, Object arg) { // 当彩票状态改变的时候,彩民需要获得通知更新 if(o != null && o instanceof LotteryData){ LotteryData lotteryData = (LotteryData)o; System.out.println("我是彩民1号->彩票日期:" + lotteryData.getDate() + ", 中奖号码为:" + lotteryData.getWinningCount()); } } }
public class BuiltInObserverPatternTest { public static void main(String[] args) { // 声明一个具体主题 final LotteryData lotteryData = new LotteryData(); // 声明观察者1号并注册 final LotteryBuyerOne lBuyerOne = new LotteryBuyerOne(lotteryData); lotteryData.beginWork(); final Timer timer = new Timer(); // 6s后彩民1号,因为总中不了奖失去了兴趣,取消注册了 timer.schedule(new TimerTask() { @Override public void run() { lotteryData.deleteObserver(lBuyerOne); timer.cancel(); } }, 6000); } }
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算