Head First Design Mode(3)-观察者(Observer)模式

该系列文章系个人读书笔记及总结性内容,任何组织和个人不得转载进行商业活动!

 

观察者(Observer)模式:

    让你的对象知悉现状——不错过感兴趣的事;

    观察者模式是JDK中使用最多的模式之一;

    我们还会介绍1对多关系及松耦合;

    

有了观察者,你将消息灵通;

 

气象观测站:

    WeatherData对象从气象观测站获取天气状况(温度,湿度,气压);

    有三种布告板(日后会向外部公布接口,由其他人扩展新的布告板),显示不同要求的天气信息;

    当WeatherData对象获得最新的测量数据时,三种布告板必须实时更新;        

 

Weather对象知道如何与气象站联系,获得相关的数据,这个我们不关注;

我们需要建立一个应用,利用WeatherData对象取得的数据,更新诸多布告板的显示;

 

一个WeatherData类:

    获取气象数据(温度,湿度,气压):getTemperature();getHumidity();getPressure();

    回调方法:measurementsChanged(); 当气象测量更新,此方法会被调用;

    

我们知道的:

    WeatherData类具有getter方法,获取气象参数;

    当新的气象数据更新时,measurementsChanged()方法会被调用;随机布告板的显示也需要更新;

    此系统需要可扩展;(添加或删除布告板)

 

一种实现方式:

    我们可以在WeatherData类中,新建三个实例变量,存储当前的三个布告板实例;

    每个布告板实例都有一个update方法,可以将传入的气象参数(温度,湿度,气压)进行显示更新;

    当measurementsChanged()方法被调用的时候,WeatherData调用getter方法获取气象参数,诸多布告板实例变量调用各自的update方法;

 

问题出在哪:

    上边是一种针对具体实现编程的方式,会导致日后增加或删除布告板的时候,修改程序;

    在这里我们看到了统一的接口:布告板的update()方法,参数可以是温度、湿度、气压等气象数据;

    改变的不同布告板的实例变量,按照我们已经学习到的原则,我们需要将改变的地方,封装起来;

 

认识观察者模式:

以报纸和杂志的订阅为例:

    报社负责出版报纸;

    向某家报社订阅报纸,只要他们有新报纸出版,就会给你送来;当不想再看报纸的时候,取消订阅;

    只要报社还在,就会一直有人(或单位)订阅或取消订阅报纸;

 

出版者+订阅者=观察者模式:

    出版者:主题(Subject);

    订阅者:观察者(Observer);

    

主题对象:管理某些数据,当这些数据改变的时候,就会通知观察者;

观察者:订阅(注册)主题 以便在主题数据改变的时候,能够收到更新;

        

观察者还可以取消订阅,主题收到取消订阅的请求后,就会把该观察者除名;对象也可以重新订阅,再次成为观察者;

 

定义观察者模式:

观察者模式:

    定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,他的所有依赖者都会收到通知并自动更新;

 

    观察者模式定义了一系列对象之间的一对多关系;

    当一个对象改变的时候,其他依赖者都会收到通知;

 

主题和观察者定义了一对多的关系,观察者依赖于此主题,只要主题状态一有改变,观察者就会被通知;

 

观察者模式的类图:

    主题接口:主题对象实现,注册观察者、从观察者中删除、通知观察者等;

        每个主题可以有很多观察者;    

    观察者接口:观察者对象实现,更新方法,主题状态改变的时候会被调用;    

 

观察者使用主题的状态,依赖主题来告诉他们这些状态何时改变了;这比很对对象控制同一份数据更容易得到干净的OO设计;

 

松耦合:

    观察者模式提供了一种对象设计,让主题和观察者之间松耦合;

    因为,主题只知道观察者实现的接口(interface-Observe),并不需要知道观察者具体类是谁,实现了那些细节;

    且,任何时候都可以增加新的观察者,主题依赖的实现Observer接口的对象列表;

    主题不在乎别的,它只会发送通知给所有实现了观察者接口的对象;

 

我们可以独立的复用主题或观察者,二者并非紧耦合;改变观察者或主题一方也不会影响另一方,因为二者松耦合;

 

设计原则:

    为了交互对象之间的松耦合设计而努力;

 

松耦合能使对象之间的依赖降到最低;

 

气象站:

设计气象站:

    每个布告板都有差异,所以需要使用同一个接口;

    布告板可以实现观察者接口统一update方法,还可以实现DisplayElement,统一展示信息的方法;

    每个布告板都应该有一个subject指针指向WeatherData对象,方便订阅和取消订阅;

 

实现气象站:

    新建接口;

    WeatherData实现主体接口;

    建立布告板;

    运行测试类;

 

启动气象站:

bogon:第二章 观察者模式 huaqiang$ javac *.java
bogon:第二章 观察者模式 huaqiang$ java WeatherDataDrive
LookGround1Display——
Conditions:
Temperature:5.2
Humidity:5.6

LookGround2Display——
Conditions:
Temperature:5.2
Humidity:5.6
Pressure13.4

LookGround2Display——
Conditions:
Temperature:5.2
Humidity:5.6
Pressure13.4

            

对于启动的代码,可以使用具体的气象站类型,布告板则使用接口类型:

public class WeatherDataDrive{
    public static void main(String[] args){
        //模拟气象变化 
        WeatherData station = new WeatherData();
        Observer display1 = new LookGround1Display(station);
        Observer display2 = new LookGround2Display(station);

        //模拟信息更新 
        station.setMeasurements((float)5.2,(float)5.6,(float)13.4);
        
        station.removeObserver(display1);
     
        //模拟信息更新 
        station.setMeasurements((float)5.2,(float)5.6,(float)13.4);   
    }
    
}

 

通过上述示例,我们可以注册观察者、取消观察者、发送通知给观察者;

现在,如果有人实现了一个新的布告板,并注册为气象站的观察者,则在气象信息变化的时候就会收到通知;

 

除了自行实现观察者模式,Java也有内置的Observer模式;

 

Java内置的观察者模式:

    java.util包 内包含基本的Observable类和Observer接口,和我们自行实现的很相似,使用上会更方便;

    查看Java文档表述如下图;

 

    通过文档的说明:

        我们的主题需要扩展自Observable类(注意这是一个继承关系),被称为“可观察者”类;

        我们的观察者需要实现Observer接口,然后调用任何Observable对象的addObserver方法(不再当的话,调用deleteObserver方法即可);

 

可观察者要如何送出通知:

    先调用setChanged()方法,标记状态已经改变的事实;

    然后调用两种notifyObservers()方法中的一个(notifyObservers()/notifyObservers(Object arg)),将数据“推”出去;

 

观察者如何接收通知:

    实现更新方法(注意方法签名);

 

如果调用notifyObservers()之前没有事先调用setChanged(),观察者就不会被通知;

    setChanged()方法,可以让你在更新观察者时,有更多的弹性;

    比如对于气象数据,当气温+-1度的时候,才认为有更新的必要性,就可以在这里做一下限制;

    clearChanged()方法将changed状态设置回false;

    另一个hasChanged()方法,会告诉changed标志的当前状态;

 

利用内置的支持重做气象站:

    import java.util.Observer;

    import iava.util.Observable;

 

    WeatherData extends Observable{}

    WeatherData的构造器方法也不再需要为记住观察者而建立数据结构了;

    对于注册、删除、通知观察者的方法也都是用父类的方法即可;

 

    LookGroundDisplay implements Observer, DisplayElement{}

    实现的update(Observable obs, Object arg)方法中,arg参数可以传各种数据,你也可已经WeatherData对象传回来,使用其状态的getter方法“拉”数据;

 

值得注意的是:

    不要依赖于观察者被通知的次序;

    同时,按照之前的设计原则,内置的Observable是一个类,这并不是一件好事:

        因为这是一个类,设计必须继承它,但如果某类向同时具有Observable类和另一个超类的行为,就比较麻烦;

        在文档中,我们注意到,setChanged()方法是受保护的(protected),这意味着,除非使用继承,否则无法创建示例并组合到自己的对象中来;我们已经知道应该“多用组合,少用继承”;

 

如果使用内置的观察者模式不方便,那就自行实现,重要的是,你已经理解了观察者模式;

 

在JDK中,观察者模式的应用举例:

JButton:

    观察一下JButton的超类AbstractButton,会看到许多增加与删除倾听者(listener)的方法;

    这些方法让观察者感应到Swing组件的不同事件类型,比如ActionListener能监听到可能发生在按钮上的动作;

 

添加观察者:

 

ActionListener接口定义的方法:

 

示例(源码):

    我们在JFrame上放了一个按钮;

    制造了了两个倾听者(观察者),观察者的类定义,使用了内部类;

    发生操作的时候 会调用actionPerformed()方法;

import javax.swing.JFrame;
import java.awt.Panel;
import javax.swing.JButton;
import java.awt.Dimension;

import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

import java.lang.Object;

public class SwingObserverExample{
    JFrame frame;
    public static void main(String[] args){
        SwingObserverExample example  = new SwingObserverExample();
        example.go();
    }
    
    public void go(){

        frame = new JFrame();
        frame.setBounds(0,0, 400, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        Panel p = new Panel();
        
        JButton button = new JButton("Should I do it?");
        button.addActionListener(new AngelListener());
        button.addActionListener(new DevilListener());
        
        Dimension preferredSize = new Dimension(200,200);
        button.setPreferredSize(preferredSize);
        
        p.add(button);
        
        frame.add(p);
        frame.setVisible(true);

    }
    
    class AngelListener implements ActionListener{
        public void actionPerformed(ActionEvent event){
            System.out.println("Don`t do it.");
        }
    }

    class DevilListener implements ActionListener{
        public void actionPerformed(ActionEvent event){
            System.out.println("Come on,do it.");
        }
    }
    
}

程序运行截图:

 

总结:

1.观察者模式定义了对象之间一对多的关系;

2.主题(可观察者)用一个共同的接口来更新观察者;

3.观察者和可观察者之间用松耦合方式结合(loosecoupling);

    可观察者不知道观察者的细节,只知道观察者实现了观察者接口;

4.使用此模式时,不可以依赖特定的通知次序;

5.Java提供了观察者模式的实现java.util.Observable;

6.有必要的话,可以自己实现自己的观察者模式;

 

OO基础:

    抽象;

OO原则:

    封装变化

    多用组合,少用继承

    针对接口编程,不针对实现编程

    ——为交互对象之间的松耦合设计而努力;

OO模式:

    ——观察者模式:在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新;

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值