设计模式学习笔记(二十三)观察者模式

设计模式学习笔记(二十三)观察者模式

引入

观察者模式的使用频率非常高,我们这边提一下,观察者模式别名订阅模式,提到订阅模式,大家有没有想到消息队列的订阅模式,在实现思路上相同的。比如我们在微信中关注的订阅号,当他们发布新文章,所有关注的用户都会收到消息,订阅号就是被观察者,关注的用户就是观察者。在此基础上我们可以进行扩展。

概念

观察者模式是一种行为型设计模式,它定义了对象之间的一对多的依赖关系,使得当一个对象的状态发生变化时,所有依赖它的对象都会得到通知并自动更新。

在观察者模式中,存在两种核心角色:
1、观察目标(Subject):也称为被观察者、发布者或主题,它是具有状态的对象,可以被其他对象(观察者)订阅并接收其状态变化的通知。
2、观察者(Observer):也称为订阅者或监听者,它是依赖于观察目标的对象,当观察目标的状态发生变化时,观察者会接收到通知,并根据通知进行相应的操作。

观察者模式的基本流程如下:
观察者向观察目标注册,表示对观察目标感兴趣,希望能够接收到其状态变化的通知。
当观察目标的状态发生变化时,它会遍历通知所有已注册的观察者,并调用它们的特定方法(比如update())来传递最新的状态信息。
观察者接收到通知后,执行相应的操作,比如读取变化后的状态或执行特定的业务逻辑。
在这里插入图片描述
(1)Subject(目标):目标又称为主题,它是指被观察的对象。在目标中定义了一个观察者集合,一个观察目标可以接受任意数量的观察者来观察,它提供一系列方法来增加和删除观察者对象,同时它定义了通知方法notify()。目标类可以是接口,也可以是抽象类或具体类。
(2) ConcreteSubject(具体目标):具体目标是目标类的子类,通常它包含有经常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知;同时它还实现了在目标类中定义的抽象业务逻辑方法(如果有的话)。如果无须扩展目标类,则具体目标类可以省略。
(3)Observer(观察者):观察者将对观察目标的改变做出反应,观察者一般定义为接口,该接口声明了更新数据的方法update(),因此又称为抽象观察者。
(4)ConcreteObserver(具体观察者):在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致;它实现了在抽象观察者Observer中定义的update()方法。通常在实现时,可以调用具体目标类的attach()方法将自己添加到目标类的集合中或通过detach()方法将自己从目标类的集合中删除。

观察者模式描述了对象之间的依赖关系,以及如何构建满足这种需求的系统。该模式包含两类对象:观察目标观察者。观察目标可以有任意数量的观察者对象,当观察目标的状态发生改变时,所有观察者都会收到通知。
观察目标是通知的发布者,它并不需要知道具体的观察者是谁,只需要在状态改变时通知所有观察者。观察者对象订阅观察目标,并对目标的状态变化做出相应的响应,以保持自身与目标状态同步。这种交互方式也被称为发布-订阅模式,观察目标相当于一个发布者,而观察者则是订阅者。
观察者模式的优点在于它实现了对象之间的松耦合,观察目标和观察者可以独立演化而不相互影响。同时,观察者模式提供了一种简单的方式来实现事件驱动的系统,其中多个观察者可以同时对同一个事件进行处理。
总的来说,观察者模式在许多场景中都非常有用,特别是当对象之间的状态变化需要通知其他对象时。它帮助我们构建可扩展、松耦合的系统,同时也提供了一种简洁的方式来实现事件驱动的编程模型。

示例

在使用微信的时候,我们会关注一些订阅号,当订阅号发布新内容的时候,关注的用户就会收到对应的提醒,因此订阅号就是被观察者,关注的用户就是观察者,当用户点赞的时候,打印记录某用户的点赞情况。
在这里插入图片描述

//抽象观察者类:用户观察者,关注公众号的人
interface UserObserver {
    public String getUserName();
    public void setUserName(String name);
    public void like(AbstractSubscribe sc); //点赞方法
    public void receive(AbstractSubscribe sc); //接收某订阅号消息的方法
}
//具体观察者类:用户观察者,关注公众号的人
class User implements UserObserver {
    private String userName;

    public User(String userName) {
        this.userName = userName;
    }

    @Override
    public String getUserName() {
        return userName;
    }

    @Override
    public void setUserName(String userName) {
        this.userName = userName;
    }

    @Override
    public void like(AbstractSubscribe sc) {
        System.out.println("用户:" + userName + "点赞了" + sc.getMessage() + "文章");
    }

    @Override
    public void receive(AbstractSubscribe sc) {
        System.out.println("用户:" + userName + " 接收到:" + sc.getMessage() + " 的消息");
    }
}
//抽象目标类:订阅号
abstract class AbstractSubscribe {
    protected String message;
    protected List<UserObserver> userList = new ArrayList<>();//记录订阅号被关注的用户

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public void concern(UserObserver user) {
        userList.add(user);
    }

    public void unConcern(UserObserver user) {
        userList.remove(user);
    }
    //声明抽象通知方法
    public abstract void notifyObserver();
}

class Subscribe extends AbstractSubscribe{

    //通知方法
    @Override
    public void notifyObserver() {
        for (UserObserver user : userList) {
            user.receive(this);
        }
    }
}

客户端

UserObserver u1,u2,u3,u4;
        u1 = new User("张三");
        u2 = new User("李四");
        u3 = new User("王五");
        u4 = new User("赵六");

        AbstractSubscribe as = new Subscribe();
        as.concern(u1);
        as.concern(u2);
        as.concern(u3);
        as.concern(u4);

        //编辑文章
        as.setMessage("文章11111");
        //发布文章
        as.notifyObserver();
        System.out.println("-------------------");
        //用户点赞
        u1.like(as);

打印结果

用户:张三 接收到:文章11111 的消息
用户:李四 接收到:文章11111 的消息
用户:王五 接收到:文章11111 的消息
用户:赵六 接收到:文章11111 的消息
-------------------
用户:张三点赞了文章11111文章

本示例中观察者Subscribe的notifyObserver()方法调用了被观察者user的receive()方法,然后将自身传入,然后在观察者user的receive()方法中调用了传入的观察者实例的方法getMessage(),这就是我们之前提到过的**“双重分派机制”**。

JDK源码

我们找到java.util.Observable类,该类就对应的是观察者。
在这里插入图片描述
在这里插入图片描述
在notifyObservers(Object arg)方法中实现了遍历。
在这里插入图片描述

我们找到java.util.Observer类
在这里插入图片描述
这个接口就是被抽象观察者,它在Observable观察者类中,作为集合元素。

观察者模式在 Java事件处理

观察者模式和Java事件处理是密切相关的。在Java中,事件处理常常使用观察者模式来实现。
Java提供了一套完整的事件处理机制,其中的关键角色与观察者模式中的概念相对应:
事件源(Event Source):也称为发布者(Publisher),它是事件的来源,通常是一个对象,能够产生或触发事件。
事件(Event):代表具体的事件对象,包含了事件发生的信息和数据。
监听器(Listener):也称为观察者(Observer),它是注册到事件源上的对象,用于处理特定类型的事件。监听器必须实现特定的接口或继承特定的抽象类,以便能够被事件源正确地调用。

Java中的事件处理流程如下:
1、事件源将事件对象实例化,并将其传递给监听器。
2、监听器注册到事件源上,通过特定的方法将自身注册为事件的处理器,告知事件源自己可以处理哪种类型的事件。
3、当事件源发生对应的事件时,会遍历已注册的监听器列表,并调用它们的事件处理方法进行处理。
监听器接收到事件后,根据需要进行相应的处理操作。
4、Java提供了多种实现事件处理的方式,包括基于接口(如java.util.EventListener)、注解(如@EventListener)和反射(如java.beans.PropertyChangeListener)等。

通过观察者模式实现的事件处理机制使得Java程序能够更加灵活地处理事件,并实现事件源与事件处理逻辑之间的解耦。它提供了一种松耦合的设计方式,使得对象之间的交互更加灵活、可扩展,能够有效地应对复杂的业务需求。

总结

优点:
1、降低了对象之间的耦合度。观察者和观察目标之间是松散耦合的,当观察目标发生变化时,不需要知道具体有哪些观察者对它感兴趣,也不需要知道观察者的具体实现,从而使得代码更加清晰并易于维护。
2、支持广播通信。一个观察目标可以同时通知多个观察者,实现一对多的通知机制。这样做的好处是观察目标只需要知道观察者列表即可,无需知道具体的观察者实现,从而简化了设计和实现。
3、支持动态添加和删除观察者。在运行时可以动态地向观察目标注册或删除观察者,增加了系统的灵活性。

缺点:
1、观察目标和观察者之间存在依赖关系,观察目标过多地依赖观察者可能会导致程序设计中出现混乱,并增加维护难度。
2、观察者可能会收到过多的通知,从而导致一些性能问题或资源浪费。
3、如果观察目标的状态发生改变时,观察者之间有相互依赖关系,可能会产生循环调用的问题。

应用场景:
1、当一个对象状态的改变需要同时改变其他对象的状态时,可以使用观察者模式。
2、当一个抽象模型有两个方面,其中一个方面依赖于另一个方面时,可以使用观察者模式实现两者之间的交互。例如MVC模式中的视图和模型之间的更新。
3、当一个对象必须通知其他对象,但是又希望这些对象不需要知道它通知了哪些对象时,可以使用观察者模式。
4、当实现类似事件处理系统的功能时,可以考虑使用观察者模式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值