模式开发艺术之观察者模式

何时使用观察者模式

1、触发联动:当修改目标状态时就会触发相应的通知,然后会循环调用所有注册的观察者对象的相应方法。

2、建议在下面三种情况下使用观察者模式:

1.当一个抽象模型有两个方面,其中一个方面的操作依赖于另一个方面的状态变化时

2.如果在封盖一个对象的时候,需要同时连带改变其他的对象,而且不知道究竟应该有对少对象需要被连带改变

3.当一个对象必须通知其他的对象,但是你又希望这个对象和其他被他通知的对象是松散耦合的

实现方式一:观察者模式 经典模版

观察者模式的结构:Subject(被观察者或者说是目标类):要有添加观察者,删除观察者,和提醒观察者(当被观察者的状态发生改变的时候调用这个方法)的方法,Observe(观察者):要有更新方法(当观察者状态发生改变调用提醒方法后观察者通过更新方法来做出不同响应(动作))

ConcreteSubject是Subject接口的实现类

ConcreteObserver是Observer接口的实现类

目标(被观察者):

/**

* 目标对象,它知道观察它的观察者,并提供注册(添加)和删除观察者的接口

*/

public class Subject {

    // 用来保存注册的观察者对象

    private List<Observer> observers = new ArrayList<Observer>();

    // attach detach notifyObservers

    // 把订阅天气的人添加到订阅者列表中

    public void attach(Observer observer) {

        observers.add(observer);

    }

    /**

    * 删除集合中的指定观察者

    * @param observer

    */

    public void detach(Observer observer) {

        observers.remove(observer);

    }

    /**

    * 通知所有注册的观察者对象

    */

    protected void notifyObservers() {

        for (Observer observer : observers) {

            observer.update(this);

        }

    }

}

目标(被观察者)具体实现:

/**

* 具体的目标对象,负责把有关状态存入到相应的观察者对象中

*/

public class ConcreteSubject extends Subject {

    // 目标对象的状态

    private String Content;

    public String getSubjectState() {

        return Content;

    }

    public void setSubjectState(String Content) {

        this.Content = Content;

        // 内容有了,通知所有的订阅的人

        this.notifyObservers();

    }

}

观察者接口:

/**

* 这是一个观察者接口,定义一个更新的接口给那些在目标发生改变的时候被通知的对象

*/

public interface Observer {

    /**

    * 更新的接口 传入目标对象,方便获取相应的目标对象的状态

    * @param subject

    */

    void update(Subject subject);

}

观察者接口的具体实现:

/**

* 具体的观察者对象,实现更新的方法,使自身的状态和目标的状态保持一致

*/

public class ConcreteObserver implements Observer {

    // 观察者的名字,是谁收到了这个讯息

    private String observerName;

    // 观察者的状态,这个消息从目标处获取

    private String observerState;

    // 提醒的内容

    private String remindTing;

    /**

    * 获取目标类的状态同步到观察者的状态中

    */

    @Override

    public void update(Subject subject) {

        observerState = ((ConcreteSubject) subject).getSubjectState();

        System.out.println(observerName + "收到了, " + observerState + " , " + remindTing);

    }

    public String getObserverName() {

        return observerName;

    }

    public void setObserverName(String observerName) {

        this.observerName = observerName;

    }

    public String getRemindTing() {

        return remindTing;

    }

    public void setRemindTing(String remindTing) {

        this.remindTing = remindTing;

    }

}

测试类:

    public static void main(String[] args) {

        // 1.创建目标

        ConcreteSubject weather = new ConcreteSubject();

        // 2.创建观察者

        ConcreteObserver observerGirl = new ConcreteObserver();

        observerGirl.setObserverName("小明的女朋友");

        observerGirl.setRemindTing("是我们的第一次约会,地点街心公园,不见不散哦");

        ConcreteObserver observerMum = new ConcreteObserver();

        observerMum.setObserverName("老妈");

        observerMum.setRemindTing("是一个购物的好日子,明天去天虹扫货");

        // 3.注册观察者

        weather.attach(observerGirl);

        weather.attach(observerMum);

        // 4.目标发布天气

        weather.setSubjectState("#明天天气晴朗,蓝天白云,气温28度#");

    }

实现方式二:利用Java提供的观察者实现 观察者模式

Java 实现 VS 自己实现的对比四点:

(1)不需要再定义观察者和目标接口(JDK已经定义)。

(2)具体的目标实现里面不需要再维护观察者的注册信息,Java中的Observable类里面已经实现。

(3)触发通知的方式有一点变化,要先调用setChanged方法,这个是Java为了帮助实现更精确的触发控制而提供的功能。

(4)具体观察者的实现里面,update方法其实能同时支持推模型和拉模型,这个Java在定义的时候,已经考虑。

目标(被观察者):

/**

* 被观察者的具体实现

*/

public class ConcreteSubject extends Observable {

    // 变化的具体内容

    private String content;

    public String getContent() {

        return content;

    }

    public void setContent(String content) {

        this.content = content;

        // 发生变化,就要通知所有的观察者

        // 注意在通知之前,在用Java中的Observer模式的时候,下面这句话不可少

        this.setChanged();

        // 然后主动通知,这里我们先用推的方式

        this.notifyObservers(content);

        // 如果是拉的方式,我们就调用

        // this.notifyObservers();

    }

}

观察者:

/**

* 具体的观察者对象

*/

public class ConcreteObserver implements Observer{

    //观察者名称的变量

    private String observerName;

    @Override

    public void update(Observable o, Object arg) {

        //第一种是推的方式

        System.out.println(observerName+"收到了消息,目标推送过来的是"+arg);

        //第二种是拉的方式

        System.out.println(observerName+"收到了消息,主动到目标对象中去拉,拉的内容是"+((ConcreteSubject)o).getContent());

    }

    public String getObserverName() {

        return observerName;

    }

    public void setObserverName(String observerName) {

        this.observerName = observerName;

    }

}

测试类:

public static void main(String[] args) {

        // 创建一个目标,也就是被观察者

        ConcreteSubject subject = new ConcreteSubject();

        // 创建小明的女朋友作为观察者

        ConcreteObserver girl = new ConcreteObserver();

        girl.setObserverName("小明的女朋友");

        // 创建小明的老妈作为观察者

        ConcreteObserver mum = new ConcreteObserver();

        mum.setObserverName("小明的老妈");

        // 注册观察者

        subject.addObserver(girl);

        subject.addObserver(mum);

        // 目标更新天气情况了

        subject.setContent("天气晴,气温28度");

    }

观察者优缺点

1、观察者模式的优点:

(1)观察者模式实现了观察者和目标之间的抽象耦合

(2)观察者模式实现了动态联动

(3)观察者模式支持广播通信

2、观察者模式的缺点:可能会引起无谓的操作。

实现方式三:区别对待观察者场景问题 (灵活定制观察者)

区别观察者模式是,目标父类不实现通知方法,在子类中实现有区别的通知方法。

区别对待的观察者模型中和通用观察者模型的区别在于:要根据不同的观察者来进行不同的推送,所以区别在于目标类中的通知更新方法需要在具体的目标类中进行实现。(因为需要根据不同的情况进行更新,所以需要在具体的目标类中实现刚刚那个方法)

区别对待观察者,逻辑判断让观察者实现更符合逻辑,定义到接口观察者中,setRule(),让小明女朋友和老妈自己去定义

目标(被观察者)的抽象方法:

public abstract class Subject {

    // 用来保存注册的观察者对象

    public List<Observer> observers = new ArrayList<Observer>();

    // attach detach abstract notifyObservers

    // 把观察者添加到订阅者列表中

    public void attach(Observer observer) {

        observers.add(observer);

    }

    // 删除集合中指定的订阅天气的人

    public void detach(Observer observer) {

        observers.remove(observer);

    }

    protected abstract void notifyObservers();

}

目标(被观察者)的具体实现:

public class ConcreteSubject extends Subject {

    // "晴天" "下雨" "下雪"

    // 目标对象的状态

    private String Content;

    @Override

    protected void notifyObservers() {

        // 循环所有注册的观察者

        for (Observer observer : observers) {

            // 情况之一:

            // 如果天气是晴天,按照小明的女朋友需要下雨的条件,小明的老妈需要下雨或下雪的条件,则她们俩就都不需要通知了。

            // 情况之二:

            // 如果天气是下雨,则小明的女朋友需要通知,而小明的老妈也需要通知。

            // 情况之三:

            // 如果天气是下雪,则只需要通知小明的老妈。

            if("下雨".equals(this.getContent())){

                if("小明的女朋友".equals(observer.getObserverName())){

                    observer.update(this);

                }

                if("小明的老妈".equals(observer.getObserverName())){

                    observer.update(this);

                }

            }

            if("下雪".equals(this.getContent())){

                if("小明的老妈".equals(observer.getObserverName())){

                    observer.update(this);

                }

            }

        }

    }

    public String getContent() {

        return Content;

    }

    public void setContent(String content) {

        Content = content;

        this.notifyObservers();

    }

}

观察者的接口:

/**

* 定义一个更新的接口方法给那些在目标发生改变的时候被通知的观察者对象调用

*/

public interface Observer {

    // 更新的接口

    public void update(Subject subject);

    // 设置观察者名称

    public void setObserverName(String observerName);

    // 取得观察者名称

    public String getObserverName();

}

观察者的具体实现:

public class ConcreteObserver implements Observer {

    // 观察者的名称

    private String observerName;

    // 天气情况的内容

    private String content;

    // 提醒的内容

    private String remindThing;

    @Override

    public void update(Subject subject) {

        content = ((ConcreteSubject) subject).getContent();

        System.out.println(observerName + "收到了<" + content + ">," + remindThing);

    }

    @Override

    public void setObserverName(String observerName) {

        this.observerName = observerName;

    }

    @Override

    public String getObserverName() {

        return observerName;

    }

    public String getContent() {

        return content;

    }

    public void setContent(String content) {

        this.content = content;

    }

    public String getRemindThing() {

        return remindThing;

    }

    public void setRemindThing(String remindThing) {

        this.remindThing = remindThing;

    }

}

测试类:

public static void main(String[] args) {

        // 1.创建目标

        ConcreteSubject weather = new ConcreteSubject();

        // 2.创建观察者

        ConcreteObserver observerGirl = new ConcreteObserver();

        observerGirl.setObserverName("小明的女朋友");

        observerGirl.setRemindThing("下雨了,安静的呆在家里吧");

        ConcreteObserver observerMum = new ConcreteObserver();

        observerMum.setObserverName("小明的老妈");

        observerMum.setRemindThing("不管下雨还是下雪,我都不出门了");

        // 3.注册观察者

        weather.attach(observerGirl);

        weather.attach(observerMum);

        // 4.目标发布天气

        weather.setContent("下雪");

    }

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

推荐阅读更多精彩内容

  • 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通...
    扈扈哈嘿阅读 1,310评论 0 12
  • 观察者模式也叫作发布-订阅模式,也就是事件监听机制。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听...
    超级大鸡腿阅读 228评论 0 0
  • 设计模式分类 总体来说设计模式分为三大类:创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原...
    lifeline丿毅阅读 1,173评论 0 2
  • 观察者模式是使用最为频繁的设计模式之一。在很多地方都有用到。比如各种编程语言的GUI事件处理实现,各种框架的实现,...
    树獭非懒阅读 15,620评论 1 7
  • 前言: 之前项目有个模块需要实现被通知的作用,那是第一时间就想到了观察者,那个模块是对象间一对一的依赖关系,...
    felicia_coder阅读 388评论 0 2