Java设计模式之行为型-观察者模式(UML类图+案例分析)

目录

一、基本概念

二、UML类图

三、角色设计

四、代码实现

案例一

案例二

案例三

五、总结


一、基本概念

观察者先订阅被观察者对象,当被观察者的状态发生变化时,观察者可以及时收到消息,在这种模式当中,被观察者维护了一个观察者列表,并提供了添加、删除、通知观察者的方法。

二、UML类图

三、角色设计

角色描述
抽象主题抽象被观察者,提供注册和移除观察者、通知观察者的接口
具体主题将有关状态存入具体观察者对象,当自己的状态发生改变时,给所有登记过的观察者发出通知
抽象观察者定义了一个更新接口,使得在得到主题更改通知时更新自己
具体观察者实现抽象观察者接口,以便在得到主题的通知时更新自身的状态

四、代码实现

这边我分享了三个案例,分别是通过自定义撰写、JDK源码封装和Spring框架封装三种去实现。

案例一

假设有一个被观察者:优快云博主,它有2个粉丝分别是Jack和Tom,当优快云发布了通知,对应的粉丝都会收到私信。

定义被观察者接口:

public interface Blogger {

    /**
     * 新增粉丝
     */
    void addFans(Fans fans);

    /**
     * 移除粉丝
     */
    void removeFans(Fans fans);

    /**
     * 通知粉丝
     */
    void sendMessage(String message);
}

被观察者具体实现类:

import java.util.ArrayList;
import java.util.List;

public class 优快云 implements Blogger {

    private List<Fans> fansList = new ArrayList<>();


    @Override
    public void addFans(Fans fans) {
        this.fansList.add(fans);
    }

    @Override
    public void removeFans(Fans fans) {
        this.fansList.remove(fans);
    }

    @Override
    public void sendMessage(String message) {
        for(Fans fans : fansList){
            fans.receiveMessage(message);
        }
    }
}

定义观察者接口:

public interface Fans {

    void receiveMessage(String message);

}

定义具体观察者实现类:

public class JackFans implements Fans {
    @Override
    public void receiveMessage(String message) {
        System.out.println("Jack收到了私信:"+message);
    }
}

public class TomFans implements Fans {

    @Override
    public void receiveMessage(String message) {
        System.out.println("Tom收到了私信:"+message);
    }
}

客户端:

public class Client{

    public static void main(String[] args) {
        Blogger blogger = new 优快云();
        Fans jackFans = new JackFans();
        Fans tomFans = new TomFans();
        blogger.addFans(jackFans);
        blogger.addFans(tomFans);
        blogger.sendMessage("优快云发布了《关于社区整顿的通知》");
        blogger.removeFans(jackFans);
        blogger.sendMessage("优快云发布了《关于博客发布调整的通知》");

    }
}

运行结果如下:

案例二

这个案例依旧是实现上述逻辑,只不过我们使用JDK提供的接口去实现,过程如下。

定义被观察者具体实现类:

import java.util.Observable;

public class 优快云 extends Observable {

    @Override
    public void notifyObservers(Object arg) {
        //修改状态为可以群发
        setChanged();
        //调用父类的notifyObservers 群发消息
        super.notifyObservers(arg);
    }
}

定义观察者具体实现类:

import java.util.Observable;
import java.util.Observer;

public class TomFans implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("Tom收到了私信:"+arg);
    }
}

import java.util.Observable;
import java.util.Observer;

public class JackFans implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("Jack收到了私信:"+arg);
    }
}

客户端:

import java.util.Observable;
import java.util.Observer;

public class Client{

    public static void main(String[] args) {
        Observable csdn = new 优快云();
        Observer tomFans = new TomFans();
        Observer jackFans = new JackFans();
        csdn.addObserver(tomFans);
        csdn.addObserver(jackFans);
        csdn.notifyObservers("《关于整顿社区的通知》");
        csdn.deleteObserver(tomFans);
        csdn.notifyObservers("《关于更新优快云社区的通知》");
    }
}

运行结果如下:

有了JDK提供的接口去实现,整体代码结构更简洁和方便了! 

案例三

这个案例依旧是上述逻辑,只不过我们这边使用Spring提供的封装事件的监听去实现,这边省去了搭建Spring框架的流程,直接看核心代码就行!

ApplicationEvent和Listener实际上就是Spring基于观察者模式设计的发布-订阅事件模型。

ApplicationEvent :自定义的事件对象,用于表示具体的事件。

具体代码如下:

import org.springframework.context.ApplicationEvent;

public class 优快云 extends ApplicationEvent {

    private String message;

    public 优快云(Object source,String message) {
        super(source);
        this.message = message;
    }

    public String getMessage(){
        return this.message;
    }

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

}

ApplicationListener:事件监听器接口,用于监听特定事件:

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class JackFans implements ApplicationListener<优快云> {
    @Override
    public void onApplicationEvent(优快云 event) {
        System.out.println("Jack收到私信:"+event.getMessage());
    }
}


import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class TomFans implements ApplicationListener<优快云> {
    @Override
    public void onApplicationEvent(优快云 event) {
        System.out.println("Tom收到私信:"+event.getMessage());
    }
}

单元测试:

ApplicationContext:Spring上下文,用于广播ApplicationEvent,并通知相关的ApplicationListener。

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;

import javax.annotation.Resource;

@SpringBootTest
class ObserverApplicationTests {

    @Resource
    private ApplicationContext applicationContext;

    @Test
    void contextLoads() {
        优快云 csdn = new 优快云(this,"发布了《关于整顿社区的通知》");
        applicationContext.publishEvent(csdn);
    }

}

运行结果如下:

通过这种方式,不同的业务逻辑模块就可以不依赖于具体的实现,只通过监听特定事件来响应,从而实现解耦。

事件驱动的编程方式还有以下优点:

1、降低耦合度

2、提高模块间的独立性和可重用性

3、易于扩展和维护

总的来说,ApplicationEvent和Listener的设计初衷就是为了解耦和提高系统的扩展性、稳定性。 

五、总结

优点:

1观察者和被观察者解耦,增强了灵活性。

2符合开闭原则,容易扩展。

3支持广播通信,一个对象状态变化会通知多个观察者对象。

4建立一套触发机制。

缺点:

1如果观察者很多,通知的开销很大。

2被观察者发送通知,无法知道有哪些观察者处理。

应用场景:

1需要一对多依赖关系的场景,一个对象状态变化需要通知其他多个对象。

2跨系统的消息交换,如消息队列的生产者和消费者。

3事件驱动型程序,如按钮点击触发的响应。

符合的设计原则:

1、单一职责原则(Single Responsibility Principle)

观察者和被观察者职责明确区分,都仅负责自己的功能。

2、开闭原则(Open Closed Principle)

可以新增观察者而不影响被观察者,扩展开放。

3、里氏替换原则(Liskov Substitution Principle)

观察者都遵循统一接口,扩展观察者不会对系统造成影响。

4、依赖倒转原则(Dependency Inversion Principle)

被观察者和观察者都依赖于抽象接口,不依赖具体实现。

5、接口隔离原则(Interface Segregation Principle)

观察者接口只定义了更新接口,避免了冗余。

综上,观察者模式建立一套触发机制和依赖关系,用于被观察对象状态变化时自动通知观察者,是一对多通信的最佳设计模式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黄团团

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值