一样,咱们仍是先看一个简单例子:建立一个窗口实现加法的计算功能。其效果以下:java
图1: 加法计算编程
Calculator.java:安全
import javax.swing.*; import javax.swing.border.BevelBorder; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; /** * Created with IntelliJ IDEA. * User: luoweifu * Date: 15-5-5 * Time: 下午9:14 * To change this template use File | Settings | File Templates. */ public class Calculator { /** * 主窗口的宽度 */ public static final int WIDTH = 500; /** * 主窗口的高度 */ public static final int HEIGHT = 100; private JFrame frameCalculator; private JEditorPane editAddend1; private JEditorPane editAddend2; private JEditorPane editResult; private JLabel labelPlus; private JButton btEqual; public Calculator() { frameCalculator = new JFrame(); } public void launchFrame() { frameCalculator.setSize(WIDTH, HEIGHT); frameCalculator.setLocationRelativeTo(null); frameCalculator.setTitle(加法计算); Container container = frameCalculator.getContentPane(); container.setLayout(new FlowLayout(FlowLayout.CENTER, 10, 10)); editAddend1 = new JEditorPane(); editAddend1.setBorder(new BevelBorder(BevelBorder.LOWERED)); editAddend2 = new JEditorPane(); editAddend2.setBorder(new BevelBorder(BevelBorder.LOWERED)); labelPlus = new JLabel(+); btEqual = new JButton(=); editResult = new JEditorPane(); editResult.setBorder(new BevelBorder(BevelBorder.LOWERED)); editResult.setEditable(false); container.add(editAddend1); container.add(labelPlus); container.add(editAddend2); container.add(btEqual); container.add(editResult); frameCalculator.setVisible(true); //frameCalculator.setDefaultCloseOperation(EXIT_ON_CLOSE); class AdditionCalculate implements ActionListener { @Override public void actionPerformed(ActionEvent e) { int add1 = Integer.parseInt(editAddend1.getText()); int add2 = Integer.parseInt(editAddend2.getText()); int result = add1 + add2; editResult.setText(result + ); } } AdditionCalculate additionCalculate = new AdditionCalculate(); btEqual.addActionListener(additionCalculate); } public static void main(String args[]) { Calculator calculator = new Calculator(); calculator.launchFrame(); } }
上面这个例子中,窗口和全部的控件建立完成以后,btEqual按钮邦定了一个监听对象additionCalculate,一旦这个按钮被点击, 就会通知additionCalculate对象,additionCalculate对象监听到点击事件,就会调用actionPerformed方法 做出相应的响应。additionCalculate是内部类AdditionCalculate的对象,AdditionCalculate实现了 ActionListener 接口。
经过上面的例子,你也许看出来了Java Swing/AWT包中窗口、控件的响应方式是一种源-监听器(Source/Listener)模式,也叫作观察者模式,这种机制常称为事件机制。服务器
Windows API能够开发窗口(界面)程序,Java经过Swing/AWT包也能够开发窗口(界面)程序,那么他们之间有什么异同呢?
1. 实现方式不一样,Windows API主要是经过回调,提供对外的接口由用户去实现对应的处理,内部由操做系统实现,咱们看不到;Java中的Swing/AWT主要源-监听器(观察者)模式,实现窗口(控件)对象与事件处理对象的邦定。
2. Windows API的消息机制有一个消息循环一直在接收消息,它是线程阻塞的。而Java的的Swing/AWT是一个通知方式,只有窗口(控件)有变化(被鼠标、键盘等触发)时才会通知监听者去处理,是非阻塞的。
3. 相同点:都有消息源——窗口(控件),都有消息处理,Windows API是窗口处理函数,Java中是监听者的处理方法,都有消息(Java叫事件Event)。若是把Windows API中消息队列和消息循环去掉,二者就很像了,就如同使用SendMessage直接把消息发送到窗口处理函数。因此,事件机制也能够认为是特殊的消息 机制。网络
既然Java中的窗口程序是经过源-监听器(观察者)模式实现的,咱们就有必要讨论一下观察者模式了。ide
观察者模式,顾名思意就是观察与被观察的关系,好比你在烧开水得时时看着它开没开,你就是观察者,开水就是被观察者;再好比说你在带小孩,你关注她 是否是饿了,是否是喝了,是否是撒尿了,你就是观察者,小孩就被观察者。观察者模式是对象的行为模式,又叫发布-订阅 (Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者 (Dependents)模式。当你看这些模式的时候,不要以为陌生,它们就是观察者模式。函数
观察者模式通常是一种一对多的关系,能够有任意个(一个或多个)观察者对象同时监听某一个对象。监听的对象叫观察者(后面提到监听者,其实就指观察者,二者是等价的),被监听的对象叫被观察者(Observable,也叫主题Subject)。被观察者对象在状态上发生变化时,会通知全部观察者对象,使它们可以作出相应的变化(如自动更新本身的信息)。
咱们就以上面提到的烧开水的一个简单生活实例来模拟一下观察者模式。
代码ObserverModule.java:this
//人,观察者 class Person { public void update(String data) { System.out.println(data + 关电源...); } } //水,被观察者 class Water { private Person person; private boolean isBoiled; public Water() { isBoiled = false; } public void SetBoiled() { isBoiled = true; notifyObserve(); } public void addObserver(Person person) { this.person = person; } public void removeObserver() { if (person != null) { person = null; } } public void notifyObserve() { if (isBoiled && person != null) { person.update(水开了,); isBoiled = false; } } } //客户端 public class ObserverModule { public static void main(String args[]) { Person person = new Person(); Water water = new Water(); water.addObserver(person); water.SetBoiled(); } }
结果以下:spa
水开了,关电源…操作系统
这个代码很是简单,水开了就会通知人,人就去关电源。但也有一个问题,就是拓展性很差,不灵活。若是咱们烧的开水不是用来喝,而用来洗澡,我就要监 测它的温度,可能50度就关电源,也可能要60度才行,这样一个监听就不够了,还监听温度的随时变化;再好比水开了以后,我不是关电源,而是让它保温。你 的updae又得改了……
因此上面这个代码拓展行是很差,但已经实现了咱们的基本想法,咱们算是咱们的第一个版本(版本)。接下来咱们再看一下,升级版:
版本2:ObserverModule.java
//观察者 interface Observer { public void update(Observable observable); } //被观察者 abstract class Observable { protected boolean isChanaged; protected List observers = new ArrayList(); public Observable() { isChanaged = false; } public void addObserver(Observer observer) { observers.add(observer); } public void removeObserver(Observer observer) { observers.remove(observer); } public void removeObservers() { observers.clear(); } public void notifyObservers() { if (isChanaged) { for (int i = 0; i < observers.size(); i ++) { observers.get(i).update(this); } isChanaged = false; } } } //人,温度监测 class TemperatureObserver implements Observer{ @Override public void update(Observable observable) { Water water = (Water)observable; System.out.println(温度: + water.getTemperature() + 状态: + water.getStatus()); System.out.println(TemperatureObserver observing...); } } class BoildObserver implements Observer { String doSomthing; BoildObserver(String doSomthing) { this.doSomthing = doSomthing; } @Override public void update(Observable observable) { Water water = (Water)observable; if (water.getTemperature() >= 100) { System.out.println(状态: + water.getStatus()); System.out.println(BoildObserver: + doSomthing); } } } //水,被观察者 class Water extends Observable{ private double temperature; private String status; public Water() { super(); this.temperature = 0; this.status = 冷水; } public Water(Observer observer) { this(); observers.add(observer); } public double getTemperature() { return temperature; } public String getStatus() { return status; } public void change(double temperature) { this.temperature = temperature; if (temperature < 40) { status = 冷水; } else if (temperature >= 40 && temperature < 60) { status = 温水; }else if (temperature >= 60 && temperature < 100 ) { status = 热水; } else { status = 开水; } this.isChanaged = true; notifyObservers(); } } //客户端 public class ObserverModule { public static void main(String args[]) { TemperatureObserver temperatureObserver = new TemperatureObserver(); BoildObserver boildObserver1 = new BoildObserver(关闭电源...); BoildObserver boildObserver2 = new BoildObserver(继续保湿...); Water water = new Water(temperatureObserver); water.addObserver(boildObserver1); water.addObserver(boildObserver2); water.change(45); water.change(80); water.change(100); } }
结果以下:
温度:45.0 状态:温水
TemperatureObserver observing…
温度:80.0 状态:热水
TemperatureObserver observing…
温度:100.0 状态:开水
TemperatureObserver observing…
状态:开水
BoildObserver:关闭电源…
状态:开水
BoildObserver:继续保湿…
经过上面这个活生生的例子,咱们总结一下观察者模式的设计。
观察者模式的类结构关系以下:
观察者模式的类图结构
在设计观察者模式的程序时要注意如下几点:
1. 要明确谁是观察者谁是被观察者,只要明白谁是关注对象,问题也就明白了。通常观察者与被观察者之间的是多对一的关系,一个被观察对象能够有多个监听对象(观察者)。如一个编辑框,有鼠标点击的监听者,也有键盘的监听者,还有内容改变的监听者。
2. Observable在发送广播通知的时候,无须指定具体的Observer,Observer能够本身决定是否要订阅Subject的通知。
3. 被观察者至少须要有三个方法:添加监听者、移除监听者、通知Observer的方法;观察者至少要有一个方法:更新方法,更新当前的内容,做出相应的处理。
注:添加监听者、移除监听者在不一样的模型中可能会有不一样命名,如观察者模型中通常,addObserver、removeObserver;在源-监听器 (Source/Listener)模型中通常是attach/detach,应用在桌面编程的窗口中,还多是attachWindow /detachWindow,或Register/UnRegister。不要被名称迷糊了,无论他们是什么名称,其实功能都是同样的,就是添加/删除观 察者。
4. 观察者模式的应用场景: <1>.对一个对象状态的更新须要其余对象同步更新;,或者一个对象的更新须要依赖另外一个对象的更新;<2>.对象仅须要将本身的更新通知给其余对象而不须要知道其余对象的细节,如消息推送。
观察者模式根据其侧重的功能还能够分为推模型和拉模式
推模型:被观察者对象向观察者推送主题的详细信息,无论观察者是否须要,推送的信息一般是主题对象的所有或部分数据。通常这种模型的实现中,会把被观察者对象中的所有或部分信息经过update的参数传递给观察者[update(Object obj) ]。
拉模型:被观察者在通知观察者的时候,只传递少许信息。若是观察者须要更具体的信息,由观察者主动到被观察者对象中获 取,至关因而观察者从被观察者对象中拉数据。通常这种模型的实现中,会把被观察者对象自身经过update方法传递给观察者 [update(Observable observable ) ],这样在观察者须要获取数据的时候,就能够经过这个引用来获取了。
其实JDK已经提供了对观察者模式接口的定义了。在java.util库里面,提供了一个Observable类以及一个Observer接口,构成JAVA语言对观察者模式的支持。咱们能够看一下Java中的源码:
Observable接口:
package java.util; public class Observable { private boolean changed = false; private Vector obs; public Observable() { obs = new Vector(); } public synchronized void addObserver(Observer o) { if (o == null) throw new NullPointerException(); if (!obs.contains(o)) { obs.addElement(o); } } public synchronized void deleteObserver(Observer o) { obs.removeElement(o); } public void notifyObservers() { notifyObservers(null); } public void notifyObservers(Object arg) { Object[] arrLocal; synchronized (this) { if (!changed) return; arrLocal = obs.toArray(); clearChanged(); } for (int i = arrLocal.length-1; i>=0; i--) ((Observer)arrLocal[i]).update(this, arg); } public synchronized void deleteObservers() { obs.removeAllElements(); } protected synchronized void setChanged() { changed = true; } protected synchronized void clearChanged() { changed = false; } public synchronized boolean hasChanged() { return changed; } public synchronized int countObservers() { return obs.size(); } }
Observer接口:
package java.util; public interface Observer { void update(Observable o, Object arg); }
经过前面的分析,再来看Java的源码,相信不会太难了。这里有个比较好的地方是Observable类中的addObserver、deleteObserver、notifyObservers等方法已经帮咱们考虑了线程同步的问题,这样更安全。
咱们再回顾一下加法计算器的例子。经过观察者模式的分析,也许你已经清楚了,AdditionCalculate的对象 additionCalculate就是观察者;JButton的对象btEqual就是被观察者,同时也是消息 源;btEqual.addActionListener(additionCalculate);就是添加监听者。ActionListener中的 public void actionPerformed(ActionEvent e)就至关于update方法,只不过参数e消息源产生的消息(事件)。
观察者模式还能够用于网络中的客户端和服务器,好比手机中的各类App的消息推送,服务端是观察者,各个手机App是被观察者,一旦服务器上的数据(如App升级信息)有更新,就会被推送到手机客户端。