设计模式二:观察者模式(发布订阅模式)

版权声明,转载请著名出处:http://www.jianshu.com/p/f88dca81c56b

设计模式系列

1. 设计模式一:单例模式

2. 设计模式二:观察者模式(发布订阅模式)

定义

观察者模式定义了一个一对多的依赖关系,能让一个或者多个观察者对象监督一个主题对象,这样一个主题对象在状态上的变化能够通知所有的依赖于此对象的那些观察者对象,使这些观察者对象能够自动更新,其本质是触发联动,别名:发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。

观察者模式的四个角色

1.抽象主题(Subject)角色:抽象主题角色把所有对观察者对象的引用保存在一个聚集(比如ArrayList对象)里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,抽象主题角色又叫做抽象被观察者(Observable)角色。一般是一个抽象类或者一个接口来实现,常见的方法attatch(),detach(),notifyObserver();java提供java.util.Observable类支持,
2.具体主题(ConcreteSubject)角色:将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者(Concrete Observable)角色。
3.抽象观察者(Observer)角色: 为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口。定义的方法有update(),Java提供了java.util.Observer接口支持,
4.具体观察者(ConcreteObserver)角色: 存储与主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态 像协调。如果需要,具体观察者角色可以保持一个指向具体主题对象的引用。

观察者模式的分类

观察者模式分为推模型和拉模型,其中区别为:
推模型:假定目标对象知道观察者需要的数据,会推送指定的数据,会使观察者对象难以复用;
拉模型:目标对象不知道观察者具体需要什么数据,因此把自身的引用传给观察者,由观察者来取值;

观察者模式的使用步骤

1.创建目标对象
2.创建观察者对象
3.观察者对象订阅目标对象
4.目标对象更新数据通知订阅者对象

何时使用观察者模式

1.当一个抽象模型,其中一个方面的操作依赖于另一个方面的状态变化
2.如果在更改一个对象的时候,需要同时连带改变其他的对象,而且不知道究竟应该有多少对象需要被连带改变时
3.当一个对象必须通知其他的对象,但是你又希望这个对象和其他被他通知的对象是松散耦合的

观察者模式的代码示例

一:观察者模式的标准代码模版

抽象主题(Subject)角色

public abstract class Subject {
    private List<Observer> observers=new ArrayList<Observer>();//用于保存已订阅的目标的观察者
    public void attach(Observer observer){//订阅目标对象
        observers.add(observer);
    }
    public void detach(Observer observer){//解除订阅关系
        observers.remove(observer);
    }
    protected void notifyObserver(){//通知已订阅的观察者
        for (Observer observer : observers) {
            observer.updata(this);
        }
    }
}

具体主题(ConcreteSubject)角色

public class ConcreteSubject extends Subject {
    private String subjectState;
    public String getSubjectState() {
        return subjectState;
    }
    public void updateChange(String subjectState) {//目标状态改变,通知订阅者
        this.subjectState = subjectState;
        notifyObserver();
    }
}

抽象观察者(Observer)角色

public interface Observer {
    public void updata(Subject subject); //目标对象发生改变时调用
}

具体观察者(ConcreteObserver)角色

public class ConcreteObserver implements Observer{
    private String subjectState;
    public void updata(Subject subject) {
        subjectState=((ConcreteSubject)subject).getSubjectState();//记录目标对象改变的状态值
    }
}

二:观察者模式的标准代码模版的情景使用

背景:老王在气象局上班,老王需要在天气良好的时候发一条消息给女朋友提醒约会,发一条消息给老妈提醒扫货(注:未书写的类均为模版代码不变的)
具体的天气被观察者,这里指代老王或气象局

public class ConcreteSubject extends Subject {
    private String subjectWeatherState;//天气状态
    public String getSubjectWeatherState() {
        return subjectWeatherState;
    }
    public void updateChangeWeatherState(String subjectWeatherState) {
        this.subjectWeatherState = subjectWeatherState;
        notifyObserver();
    }
}

具体的观察者,这里指代女朋友和老妈

public class ConcreteObserver implements Observer{
    private String subjectWeatherState;//天气状态
    private String subjectName;//订阅者的名字,get、set方法省略
    private String redim;//需要提示的消息  ,get、set方法省略
    public ConcreteObserver(String subjectName, String redim) {
        this.subjectName = subjectName;
        this.redim = redim;
    }
    public void updata(Subject subject) {
        subjectWeatherState=((ConcreteSubject)subject).getSubjectWeatherState();
        System.out.println(subjectName+"收到了消息,内容:"+subjectWeatherState+","+redim);
    }
}

测试使用类

public class ClientTest {
    public static void main(String[] args) {
        // 1.创建目标对象
        ConcreteSubject subject=new ConcreteSubject();
        // 2.创建观察者对象
        ConcreteObserver observerGirl=new ConcreteObserver("女朋友", "第一次约会,地点欢乐谷,不见不散");
        ConcreteObserver observerMum=new ConcreteObserver("老妈", "沃尔玛超市打折,扫货");
        // 3.观察者对象订阅目标对象
        subject.attach(observerGirl);
        subject.attach(observerMum);
        // 4.目标对象更新数据通知订阅者对象
        subject.updateChangeWeatherState("天气晴朗,蓝天白云,气温23度");
    }
}

测试的结果:

女朋友收到了消息,内容:天气晴朗,蓝天白云,气温23度,第一次约会,地点欢乐谷,不见不散
老妈收到了消息,内容:天气晴朗,蓝天白云,气温23度,沃尔玛超市打折,扫货

三:Java提供支持的观察者的情景使用

背景:一个商品价格变化需要通知已经订阅的VIP顾客
具体的目标对象(被观察者),指代商品

import java.util.Observable;
public class ProductSubject extends Observable{
    private String name;//商品名字
    private float price;//商品价格
    public ProductSubject(String name){
        this.name=name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public float getPrice() {
        return price;
    }
    public void setPrice(float price) {
        this.price = price;
        setChanged();//设置变化点,必须在通知之前调用
        notifyObservers();
    }
}

具体的观察者,指代VIP顾客

import java.util.Observable;
import java.util.Observer;
public class VipObserver  implements Observer{
    private String customerName;//顾客姓名
    public VipObserver(String customerName){
        this.customerName=customerName;
    }
    /**
     * @param observable
     *            拉模式的情况,目标对象
     * @param obj
     *            推模式的情况,指定的内容
     */
    public void update(Observable observable, Object obj) {
        String name = ((ProductSubject)observable).getName();
        float price = ((ProductSubject)observable).getPrice();
        System.out.println(customerName+"收到商品:"+name+" 的价格变化通知,现在的价格为:"+price+" 元");
    }
}

测试使用类,用于测试商品价格发生变化会通知Vip客户

public class ClientTest {
    public static void main(String[] args) {
        //1.创建被观察对象即目标对象
        ProductSubject subject=new ProductSubject("苹果");
        //2.创建观察者       
        VipObserver observerM=new VipObserver("老马哥");
        VipObserver observerW=new VipObserver("隔壁老王");
        //3.观察者订阅目标对象
        subject.addObserver(observerM);
        subject.addObserver(observerW);
        //4.目标对象发生改变
        subject.setPrice(10.5f);
    }
}

测试的结果:

隔壁老王收到商品:苹果 的价格变化通知,现在的价格为:10.5元
老马哥收到商品:苹果 的价格变化通知,现在的价格为:10.5元

观察者模式的总结

观察者的优点

1.实现了观察者和目标之间的抽象耦合
2.实现了动态的联动
3.支持广播通信

观察者的缺点

1.可能会引起无谓的操作,有些订阅者不需要响应(区别对待观察者)
2.在Java中消息的通知一般是顺序执行,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步实现。

Android中常见的观察者模式

OnClickListener、ContentObserver、android.database.Observable等;还有组件通讯库RxJava、RxAndroid、EventBus;Adapter的notifyDataSetChanged()

我的CSDN博客地址:http://blog.csdn.net/wo_ha/article/details/77717887

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容