java观察者模式

来源:互联网 发布:java开源项目代码 编辑:程序博客网 时间:2024/06/06 09:59

为了说明为什么要用观察者模式以及使用观察者模式的好处,我们先来考虑如何用程序来模拟如下的场景。

本文借鉴尚学堂马士兵老师对观察者模式讲解整理而得。

1.小孩在睡觉
2.醒来后要求吃东西

第一种设计方式:

首先来分析需要建哪些类和方法,小孩(Child),醒来(wakeUp),大人(Father),喂食(feed)。

我们让Child是一个线程类,sleep一定时间就醒来。让大人也是一个线程类,不断的循环,看小孩是否醒来,醒来就喂食。

public class Child implements Runnable {private Boolean isWakeUp = false;public void run(){try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}wakeUp();}private void wakeUp() {isWakeUp = true;System.out.println("我醒了……要吃东西了……");}public Boolean getIsWakeUp() {return isWakeUp;}}

public class Father implements Runnable{private Child child;public Father(Child child) {this.child = child;}public void run() {while(true){if(child.isWakeUp()){feed();break;}}}private void feed() {System.out.println("喂食了……");}}

客户端

public class Test {public static void main(String[] args) {Child child = new Child();Father father = new Father(child);new Thread(child).start();new Thread(father).start();}}

五秒过后就会打印如下信息

我醒了……要吃东西了……
喂食了……


第二种设计方式:

我们让child持有father的引用,child一醒就调用father的feed方法。为了我们的描述和观察者模式更为接近,我们对上面的类结构做一些改变。增加一个记录小孩醒来当前环境的类WakenUpEvent,如记录醒来时间,醒来地点,哪个小孩醒来。

class WakenUpEvent {private long time;//醒来时间private String loc;//醒来地点private Child source;//哪个小孩醒来public WakenUpEvent(long time, String loc, Child source) {super();this.time = time;this.loc = loc;this.source = source;}public long getTime() {return time;}public void setTime(long time) {this.time = time;}public String getLoc() {return loc;}public void setLoc(String loc) {this.loc = loc;}public Child getSource() {return source;}public void setSource(Child source) {this.source = source;}}

增加一个观察者接口WakenUpListener,现在的情况是只要实现了WakenUpListener的人就可以给小孩喂食。将喂食方法feed 改为actionToWakenUp,表示现在孩子醒来我不单可以喂食还可以做其他操作,增加程序的可扩展性。

interface WakenUpListener {public void actionToWakenUp(WakenUpEvent wakenUpEvent);}

Father类实现WakenUpListener接口,就不用线程了。

class Father implements WakenUpListener {public void ActionToWakenUp(WakenUpEvent wakenUpEvent) {System.out.println("feed child"+wakenUpEvent.getSource().getName());}}


public class Child implements Runnable {private String name = "小红";private WakenUpListener listener ;public Child(WakenUpListener listener) {this.listener = listener;}public void run(){try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}wakeUp();}private void wakeUp() {System.out.println("我醒了……要吃东西了……");listener.ActionToWakenUp(new WakenUpEvent(System.currentTimeMillis(),"bed",this));}public String getName() {return name;}}

客户端

public class Test {public static void main(String[] args) {Child child = new Child(new Father());new Thread(child).start();}}

5秒之后打印结果

我醒了……要吃东西了……
feed child小红


再来看第三种设计方式:

第三种设计方式我要让程序更具有扩展性。更容易的让多个观察者可以同时监听并处理小孩醒来的动作,不只是Father,而且根据不同的观察者作出不动的动作。child类让它持有监听者的一个引用的集合,也就是观察者注册。并且添加一个可以添加观察者的方法addWakenUpListener

class Child implements Runnable {private String name = "小红";private List<WakenUpListener> wakenUpListeners = new ArrayList<WakenUpListener>();public Child addWakenUpListener(WakenUpListener l) {wakenUpListeners.add(l);return this;}public String getName() {return name;}void wakeUp() {System.out.println("我醒了………");for(int i=0; i<wakenUpListeners.size(); i++) {WakenUpListener l = wakenUpListeners.get(i);l.ActionToWakenUp(new WakenUpEvent(System.currentTimeMillis(), "bed", this));}}public void run() {try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}this.wakeUp();}}



下面两个类和第二种设计方式一样

interface WakenUpListener {public void ActionToWakenUp(WakenUpEvent wakenUpEvent);}

class WakenUpEvent {private long time;//醒来时间private String loc;//醒来地点private Child source;//哪个小孩醒来public WakenUpEvent(long time, String loc, Child source) {super();this.time = time;this.loc = loc;this.source = source;}public long getTime() {return time;}public void setTime(long time) {this.time = time;}public String getLoc() {return loc;}public void setLoc(String loc) {this.loc = loc;}public Child getSource() {return source;}public void setSource(Child source) {this.source = source;}}

下面是观察者类,也就是要注册到小孩持有的集合里的类,这些类可以同时对小孩醒来的事件做出不同的反应

class Father implements WakenUpListener {public void ActionToWakenUp(WakenUpEvent wakenUpEvent) {System.out.println("feed child"+wakenUpEvent.getSource().getName());}}class GrandFather  implements WakenUpListener {public void ActionToWakenUp(WakenUpEvent wakenUpEvent) {System.out.println("hug child");}}class Dog implements WakenUpListener {public void ActionToWakenUp(WakenUpEvent arg0) {System.out.println("wang wang ...");}}

客户端

public class Test {public static void main(String[] args) {Child c = new Child();c.addWakenUpListener(new Dad()).addWakenUpListener(new GrandFather()).addWakenUpListener(new Dog());new Thread(c).start();}}
5秒之后打印结果:

我醒了………
feed child小红
hug child
wang wang ...

到此我们针对本文开头的场景的三种设计方式就分析完了。对比我们发现,第二,第三种比第一种更灵活。而第三种我们用到了观察者模式,使程序更具扩展性,低耦合。可以不修改源码实现对小孩的任意监听处理,观察不难发现,我们如果将chid类提取出一个抽象类比如Observerable,让child去实现它。那们我们就可以让任何实现Observerable类的对象得到监听,这样我们就比较完整的使用了观察者模式。

下面我们再来分析一下jdk古老的AWT是怎么用观察者模式的。先回顾一下awt中button是怎么使用的。

import java.awt.Button;import java.awt.Frame;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent;public class TestFrame extends Frame {public void launch() {Button b = new Button("press me");b.addActionListener(new MyActionListener());b.addActionListener(new MyActionListener2());this.add(b);this.pack();this.addWindowListener(new WindowAdapter(){@Overridepublic void windowClosing(WindowEvent e) {System.exit(0);}});this.setVisible(true);}public static void main(String[] args) {new TestFrame().launch();}private class MyActionListener implements ActionListener {public void actionPerformed(ActionEvent e) {System.out.println("button pressed!");}}private class MyActionListener2 implements ActionListener {public void actionPerformed(ActionEvent e) {System.out.println("button pressed 2!");}}}

我们再来用自己的方式模拟一下这个button

class Button {private List<ActionListener> actionListeners = new ArrayList<ActionListener>();public void buttonPressed() {ActionEvent e = new ActionEvent(System.currentTimeMillis(),this);for(int i=0; i<actionListeners.size(); i++) {ActionListener l = actionListeners.get(i);l.actionPerformed(e);}}public void addActionListener(ActionListener l) {actionListeners.add(l);}}interface ActionListener {public void actionPerformed(ActionEvent e);}class MyActionListener implements ActionListener {public void actionPerformed(ActionEvent e) {System.out.println("button pressed!");}}class MyActionListener2 implements ActionListener {public void actionPerformed(ActionEvent e) {System.out.println("button pressed 2!");}}class ActionEvent {long when;Object source;public ActionEvent(long when, Object source) {super();this.when = when;this.source = source;}public long getWhen() {return when;}public Object getSource() {return source;}}

客户端

public class Test {public static void main(String[] args) {Button b = new Button();b.addActionListener(new MyActionListener());b.addActionListener(new MyActionListener2());b.buttonPressed();}}


0 0