java观察者模式:如何通知事物的变化

757 阅读6分钟

517871628511674_.pic.jpg

这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战

欢迎来到今天的学习,今天我们一起来学习下使用频率很高且实践比较多的一种模式----观察者模式。多唠叨几句,我本月将会对java的设计模式精讲,欢迎点击头像,关注我的专栏,我会持续更新,加油!

系列文章:

设计模式之单例模式

设计模式之工厂模式

设计模式之建造者模式

设计模式之代理模式

设计模式之访问者模式

设计模式之适配器模式

设计模式之命令者模式

java状态模式 | 随时随地监控状态改变

...持续更新中

话不多说,进入正题

观察者模式

观察者模式是一种非常流行的设计模式,也有人称之为订阅-发布模式,我个人认为都是一种思想,就是一对象当中,任何的改变,都能被监听到,识别到,然后去通知改变,这个行为也可以称之为“事件”,观察者通过处理每一个事件来完成自身的操作处理

观察者模式的原始定义是:定义对象之间的一对多依赖关系,这样当一个对象改变状态时,它的所有依赖项都会自动得到通知和更新。

注意:该定义当中,对象之间的是一对多,那么这个一就是观察者,只有一个观察者在观察动态,多便是指被观察者。我们还可以称观察者模式为以下几种叫法,不过不管怎么叫,本质都是观察者模式的思想

  • 事件发布,事件监听

  • 发布者-订阅者

  • 生产者-消费者

这种模式在各种框架源码当中也屡见不鲜,很适合在实践中运用与巧妙处理。

我以前写过一篇干货实战文章,就是利用Spring当中的监听来完成的,有兴趣的可以看下:

干货!SpringBoot利用监听事件,实现异步操作

回归正题,下面看下,观察者模式的图(图片来源于网路)

image.png

从上图中,我们能看出观察者模式包含的四个关键角色:

1 、发布者(Publisher),也被叫作主题、被订阅者、被观察者等

2、 具体发布者(PublisherImpl),实现了发布者定义的方法的具体实现类

3、订阅者(Observer):也叫作观察者,它会存储一个注册列表,用于存放订阅者。当发布者发布消息或事件时,会通知到订阅者进行处理。

4、 具体订阅者(ObserverImpl):实现具体定义的方法操作。

接下来我们通过一个场景和代码来让你深入理解下:

场景分析及代码展示

使用场景分析:

我们拿微信公众号推文场景来展示下,我们都知道我们的公众号推文文章只会推送给关注我公众号的人,那么,假如我今天发表一篇名为“java观察者模式”一篇推文,张三,李四,王五关注了我的公众号,那么我点击发布,就会往他们的微信上推。

我们首先定义观察者 ArticleObserver。它只有一个 send 方法,当有文章 Article 发送过来时就会调用该方法。

public interface ArticleObserver {
    void send(Articl m);
}

接着再定义文章的具体数据结构,这里使用一个 content 文章内容。

public class Articl {
    final String content;
    public Articl (String m) {
        this.content = m;
    }
    public String getContent() {
        return content;
    }
}

然后,再来定义一个被观察者 Subject ,它定义了三个方法:增加观察者方法 attach、删除观察者方。

public interface Subject {
    void attach(ArticleObserver a);  //增加观察者
    void detach(ArticleObserver a);  //删除观察者
    
    void notifySend(Article m);    //异步发送推文
}

再接下来,实现 Subject 的具体发布者类 ArticlePublisher,它持有一组观察者 ArticleObserver 实例,可以通过 attach 和 detach 接口方法新增和删除观察者,异步通知发送方法 notifySend

public class ArticlePublisher implements Subject {
    private List<ArticleObserver> observers = new ArrayList<>();
    @Override
    public void attach(ArticleObserver o) {
        observers.add(o);
    }
    @Override
    public void detach(ArticleObserver o) {
        observers.remove(o);
    }
    
    @Override
    public void notifySend(Articl m) {
        observers.forEach(x->x.sed(m));
    }
}

最后,我们实现三个具体订阅者类(张三,李四,王五),它们都实现(关注了该公众号)了 ArticleObserver 接口,分别要发送到三者微信当中。

//张三订阅
public class ZhangSanSubscriber implements ArticleObserver {
    @Override
    public void send(Articl a) {
        System.out.println("ZhangSanSubscriber 看到 :: " + a.getContent());
    }
}

//李四订阅
public class LisiSubscriber implements ArticleObserver {
    @Override
    public void send(Articl a) {
        System.out.println("LisiSubscriber  看到 :: " + a.getContent());
    }
}

//王五订阅
public class WangwuSubscriber implements ArticleObserver {
    @Override
    public void send(Articl a) {
        System.out.println("WangwuSubscriber 看到 :: " + a.getContent());
    }
}

单元测试下:


public class Client {
    public static void main(String[] args) {
        ArticleObserver zhangSan = new ZhangSanSubscriber();
        ArticleObserver liSi = new LisiSubscriber();
        ArticleObserver wangWu = new WangwuSubscriber();
        Subject p = new ArticlePublisher();
        
        // 增加订阅,将相应订阅者添加进去(集合)
        p.attach(zhangSan);
        p.attach(liSi);
        p.attach(wangWu);
        
        //发送推文
        p.notifySend(new Articl("java观察者模式")); 
    }
}

//输出结果
ZhangSanSubscriber 看到 :: java观察者模式
LisiSubscriber     看到 :: java观察者模式
WangwuSubscriber   看到 :: java观察者模式

注意,我这里只是用了一篇文章(发布对象),可以多种。

OK,代码到这里就结束了。

上面的代码实现非常简单,但是充分体现了观察者模式的使用场景。观察者模式使用场景的特点在于找到合适的被观察者,定义一个通知列表,将需要通知的对象放到这个通知列表中,当被观察者需要发起通知时,就会通知这个列表中的所有“人”

总结

使用观察者模式的优点:

  • 由利用代码扩展性的提升,由于观察者和被观察之间是抽象耦合,所以扩展性较强

  • 降低系统与系统之间的耦合性,比如,建立订阅者集合,可以随时添加或者删除该集合当中的某一元素。

有褒有贬:

我个人理解,会降低性能,观察者模式通常需要事件触发,当观察者对象越多时,被观察者需要通知观察者所花费的时间也会越长,这样会在某种程度上影响程序的效率。

看到这里可能会有伙伴问了,为什么使用观察者模式?,我认为有以下两点:

1、为了方便捕获观察对象的变化并及时做出相应的操作,比如快递的状态要及时变更等。

2、上面也说到了,为了提高代码的扩展性

弦外之音

感谢你的阅读,如果你感觉学到了东西,麻烦您点赞,关注。

我已经将本章收录在专题里,点击下方专题,关注专栏,我会每天发表干货,本月我会持续输入设计模式。

加油! 我们下期再见!