设计模式-观察者模式及Java实例

设计模式-观察者模式及Java实例

转载请注明出处: http://blog.csdn.net/qq_1050087728/article/details/69675826

观察者模式是一种软件设计模式,其中称为主题的对象维护其依赖关系列表(观察者),并通常通过其中一种方法来自动通知他们任何状态的更改,观察者模式也称为发布-订阅模式。
它主要用于在“事件驱动”软件中实现分布式事件处理系统。大多数现代语言(如Java和C#)都内置了“事件”结构,实现了观察者模式组件,便于编程和简化编码。
观察者模式也是熟悉的MVC(model-view-controller)架构模式的关键部分。在许多程序库和系统中都实现了观察者模式,包括几乎所有的GUI工具包。

优点VS缺点

优点:在系统中可能会有很多类共享一种数据,如双十一天猫的交易额数据会用于不同的展板来呈现,有纯数字,折线图,柱状图。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用带来不便。观察者模式就是解决此类问题的。
缺点:观察者模式可能导致内存泄漏,称为失败的侦听器问题,因为在基本的实现中,它需要显示注册和显示注销,如在处理模式中,因为主体对观察者有很强的引用,使其保持活动。这种情况可以通过对观察者的弱引用来阻止。

耦合性和发布-订阅模式的实现

通常情况下,观察者模式是通过状态变化被观察的“主题”对象来实现的,并在发生变化时传达给观察者。这种类型的实现被认为是“紧密耦合”的,强制观察者和主题彼此了解并访问其内部部分。可能导致可扩展性,速度,消息恢复和维护(也称为丢失事件或通知)的问题,缺乏条件分散的灵活性和可能阻碍所期望的安全措施。在发布订阅模式(也称为pub-sub模式)的一些(非轮询)实现中,这通过创建专用的“message queue”(消息队列)服务器并且有时是额外的“message handler”(消息处理程序)对象来解决,作为添加阶段在观察者和正在被检查状态的被观察对象之间,,从而“解耦”软件组件。在这些情况下,消息队列服务器由具有观察者模式的观察者访问,“订阅某些消息”只知道预期消息(在某​​些情况下不知道),但是不知道消息发送者本身,而且发件人对接收者一无所知。
观察者模式,如同GOF(Erich Gamma,Richard Helm,Ralph Johnson,John Vlissides)在设计模式一书《Design Patterns》中所描述的一样,它只是一个非常基本的概念,不会在通知观察者之前或之后处理遵守删除或任何有条件或复杂的逻辑处理。该模式也不涉及记录“事件”,异步传递通知或保证其被接收。这些问题通常在消息队列系统中处理,其中观察者模式只是一小部分,当然,这都是后话。

观察者模式UML类图

观察者模式UML类图
图1 观察者模式UML图

从图1观察者模式UML类图可以看到模式中以下角色:
抽象主题(Subject):它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
具体主题(ConcreteSubject):将有关状态存入具体观察者对象并在主题内部状态改变时,给所有注册过的观察者发出通知。
抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己,其中定义了一个update():void更新方法,由具体的观察者实现更新逻辑。
具体观察者(ConcreteObserver):实现抽象观察者的更新接口,更新自己的状态与主题状态协调。

观察者模式示例ObserverDemo

如今的帝都交通非常拥挤,为了缓解交通拥挤的情况,市政府推出了限行的政策。这里我们以单双号限行为例,来说明观察者模式。限行规则如下:周一到周五限行,周六周日不限行。其中周一、周三和周五单号限行,周二周四双号限行。
代码目录如图2
ObserverDemo工程目录
图2 观察者模式工程结构目录

观察者模式代码在src源码文件夹,main()函数在test文件夹中;
interf包下是主题接口Subject,观察者接口Observer和一个观察者信息展示的接口DisplayElement;
subject包下定义了具体的主题LimitVehicleSubject,实现了Subject接口;
object包下新建了三个具体的观察者对象,实现了Observer,DisplayElement接口;
test包下是测试的类,是程序入口main()函数所在类;

1)首先建立一个抽象主题接口Subject,包含基本的三个方法registerObserver(observer),unRegisterObserver(observer),notifyObservers(observer)

package interf;

//主题接口Subject
public interface Subject {

    //注册观察者
    public void registerObserver(Observer observer);

    //注销观察者
    public void unRegisterObserver(Observer observer);

    //通知观察者
    public void notifyObservers();
}

2)然后新建一个具体的主题类LimitVehicleSubject实现了Subject接口。它持有观察者集合的引用observers,并实现了注册,注销,通知观察者的方法,还定义了一个设置主题数据的方法。

package subject;

import java.util.ArrayList;

import interf.Observer;
import interf.Subject;

//具体的主题对象
public class LimitVehicleSubject implements Subject {

    private ArrayList<Observer> observers; //定义观察者集合
    private int day;                       //定义周几的变量信息day

    //构造函数,初始化观察者集合
    public LimitVehicleSubject(){
        observers=new ArrayList<Observer>();
    }

    //更新主题状态数据的方法
    public void setDate(int day){
        //设置日期
        if(day<1||day>7){
            throw new IllegalArgumentException("Please input day between 1 and 7.");
        }
        this.day=day;
        //通知观察者,使他们能够更新自己的数据
        notifyObservers();
    }

    //注册观察者
    @Override
    public void registerObserver(Observer observer) {
        //判断传入的observer是否为空
        if(observer==null){
            throw new IllegalArgumentException("The observer is null");
        }
        //持有锁observers的线程才可以在该同步代码块中执行,此处主线程持有observers
        synchronized(observers){

            if(observers.contains(observer)){
                throw new IllegalArgumentException("Observer"+observer+"is already registered.");
            }
            observers.add(observer);
        }
    }

    //注销观察者
    @Override
    public void unRegisterObserver(Observer observer) {
        //判断传入的observer是否为空
        if(observer==null){
            throw new IllegalArgumentException("The observer is null");
        }
        //持有锁observers的线程才可以在该同步代码块中执行,此处主线程持有observers
        synchronized(observers){
            int index=observers.indexOf(observer);
            if(index==-1){
                throw new IllegalArgumentException("Observer"+observer+"was not registered.");
            }
            observers.remove(index);
        }
    }

    //通知观察者更新
    @Override
    public void notifyObservers() {
        // 通知所有注册的观察者
        synchronized(observers){
            for(int i=observers.size()-1;i>=0;i--){
                observers.get(i).update(day);
            }
        }
    }

}

3)至此,完成了主题的创建,接下来创建观察者接口Observer,包含一个更新数据的update方法。

package interf;

//接口Observer
public interface Observer {

    //更新方法
    public void update(int day);
}

4)然后创建一个展示数据的接口DisplayElement,包含展示方法display();

package interf;

//展示限行详情的接口
public interface DisplayElement {

    //展示方法
    public void display();
}

5)接下来,创建三个具体的观察者,DoubleLimitObserver,SingleLimitObserver,NoLimitObserver,实现了Observer,Displayment接口。

观察者DoubleLimitObserver

package object;

import interf.DisplayElement;
import interf.Observer;

public class DoubleLimitObserver implements Observer, DisplayElement {

    private int day;
    @Override
    public void display() {
        //求余判断是不是双号
        if(day%2==0&&day!=6){
            System.out.println("Today is "+day+",double limit");
        }
    }

    @Override
    public void update(int day) {
        //更新数据并展示
        this.day=day;
        display();
    }

}

观察者SingleLimitObserver

package object;

import interf.DisplayElement;
import interf.Observer;

public class SingleLimitObserver implements Observer, DisplayElement {

    private int day;

    @Override
    public void display() {
        // 求余判断是不是单号
        if(day%2==1&&day!=7){
            System.out.println("Today is "+day+",single limit.");
        }
    }

    @Override
    public void update(int day) {
        // 更新
        this.day=day;
        display();
    }

}

观察者NoLimitObserver

package object;

import interf.DisplayElement;
import interf.Observer;

public class NoLimitObserver implements Observer, DisplayElement {

    private int day;

    @Override
    public void display() {
        // 判断是不是周六日
        if(day==6||day==7){
            System.out.println("Today is "+day+",no limit.");
        }
    }

    @Override
    public void update(int day) {
        // 更新数据
        this.day=day;
        display();
    }

}

6)创建完主题和观察者后,新建一个测试实例ObserverTest对观察者模式进行测试,如下

package test;
import object.*;
import subject.LimitVehicleSubject;

public class ObserverTest {

    public static void main(String[] args) {

        // 创建主题,被观察者,通常是一个自身状态属性可变的类
        LimitVehicleSubject limitVehicleSubject=new LimitVehicleSubject();

        //创建三个观察者实例
        DoubleLimitObserver doubleLimitObserver=new DoubleLimitObserver();
        SingleLimitObserver singleLimitObserver=new SingleLimitObserver();
        NoLimitObserver noLimitObserver=new NoLimitObserver();

        //向主题limitVehicleSubject注册三个观察者,监听变化
        limitVehicleSubject.registerObserver(doubleLimitObserver);
        limitVehicleSubject.registerObserver(singleLimitObserver);
        limitVehicleSubject.registerObserver(noLimitObserver);

        //手动的改变限行日期
        limitVehicleSubject.setDate(1);
        limitVehicleSubject.setDate(4);
        limitVehicleSubject.setDate(7);
    }
}

7)至此,运行程序得到结果如下

Today is 1,single limit.
Today is 4,double limit
Today is 7,no limit.

Demo的github链接: ObserverDemo源码

若想了解更多关于观察者模式的应用,请关注我的下一篇博客Android源码分析:Android中的设计模式——观察者模式

转载请标明出处 http://blog.csdn.net/qq_1050087728/article/details/69675826

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值