Java 观察者模式理解

之前一直说什么观察者模式观察者模式, 其实没有那么难, 今天这篇文章我让你彻底明白什么是观察者模式, 然后灵活应用,同时呢也加深下以前自己对观察者模式的理解哈,好废话说太多了, 干就完事

观察者模式定义

对象之间存在一对多的依赖关系, 当你一(一代表的发起源)这边发生状态变更的时候就会触发(也可以说通知)到多(多指的是多个观察者)这边做出相应的业务执行
 

定义三个名词

  • 数据源
  • 发起数据源动作的人(发布者)
  • 观察者
啥是数据源?

就是需要准备好的数据,一般都是我们封装好的实体类,或者是DTO类,Request类等等

啥是发起数据源动作的人?

简单说就是发布公告的人,其实就是定义一个方法调用具体 观察者的业务处理方法 ,这个时候你就需要把我们上述创建好的数据源发送个具体观察者做一序列的业务处理, 此实现体现Java高内聚,低耦合思想

啥是观察者?

这个就不用说了吧, 就是我们实现某一块具体业务的类

 

案例演示

我们这里准备有很多类型的流水, 每一种类型的流水对应发送给不同的组 , 该组会对这种类型的流水做特定的业务处理

定义一个流水实体类

上面所说的数据源就是TransactionDataDTO,这会发送给我们的观察者

@Data
public class TransactionData implements Serializable {

    public Integer locationId;
    public String locationName;
    public Integer stockNum;

    public TransactionData(Integer locationId, Integer stockNum,String locationName) {
        this.locationId = locationId;
        this.locationName = locationName;
        this.stockNum = stockNum;
    }
    
public class TransactionDataDTO implements Serializable {

    private String transType;
    private TransactionData data;

    public TransactionDataDTO(String transType, TransactionData data) {
        this.transType = transType;
        this.data = data;
    }
}

发起数据源动作的人(发布者)

发布者将会把观察者绑定起来, 然后调用观察者的具体方法执行业务,这里我们可以将公共的绑定和拆分方法提取到抽象方法

public interface SendTxDataPublisher {
    void addObserver(RecTxDataObserver observer);
    void removeObServer(RecTxDataObserver observer);
    void notifyObservers();
}

//抽取公共的绑定和删除方法
@Data
public abstract class AbstractSendTxDataPublisher implements SendTxDataPublisher {

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

    @Override
    public void addObserver(RecTxDataObserver observer) {
        observers.add(observer);
    }

    @Override
    public void removeObServer(RecTxDataObserver observer) {
        observers.remove(observer);
    }

    public abstract void notifyObservers();
}

//发布数据源数据的人
@Data
public class RealSendTxDataPublisher extends AbstractSendTxDataPublisher {

    private TransactionDataDTO source;

    public RealSendTxDataPublisher(TransactionDataDTO source) {
        this.source = source;
    }

    @Override
    public void notifyObservers() {
        this.getObservers().forEach(e->e.doExcute(this));
    }
}
定义观察者

观察者要开始执行自己特定的业务逻辑处理, 这里定义了两个观察者, 一个Fms观察者, 一个是Pms观察者,他们会根据传入的transType做一定给数据源过滤,然后执行自己的业务

public interface RecTxDataObserver {
    void doExcute(RealSendTxDataPublisher source);
}

//Fms观察者
public class RecTxDataFmsObserver implements RecTxDataObserver {

    private RealSendTxDataPublisher source;

    private static final String[] TYPEs = {
            TransTypeEnum.Order_InStall.name(),
            TransTypeEnum.Delivery.name()
    };

    public RecTxDataFmsObserver(RealSendTxDataPublisher source) {
        this.source = source;
    }

    @Override
    public void doExcute(RealSendTxDataPublisher source) {

        TransactionDataDTO dto = source.getSource();
        TransactionData data = source.getSource().getData();

        if (Arrays.asList(TYPEs).contains(dto.getTransType())) {
            System.out.println("财务组:接收到数据类型是:transType="+dto.getTransType()+":仓库名称是:"+data.getLocationName()+",仓库ID是:"+data.getLocationId());
            System.out.println("开始执行财务组的业务...");
        }

    }
}


Pms观察者
public class RecTxDataPmsObserver implements RecTxDataObserver {

    private RealSendTxDataPublisher source;

    /**
     * 采购组只接受这几种类型的业务处理,比上述多处理一种Order_Received的业务 
     */
    private static final String[] TYPEs = {
            TransTypeEnum.Order_InStall.name(),
            TransTypeEnum.Delivery.name(),
            TransTypeEnum.Order_Received.name()

    };

    public RecTxDataPmsObserver(RealSendTxDataPublisher source) {
        this.source = source;
    }

    @Override
    public void doExcute(RealSendTxDataPublisher source) {

        TransactionDataDTO dto = source.getSource();
        TransactionData data = source.getSource().getData();

        if (Arrays.asList(TYPEs).contains(dto.getTransType())) {
            System.out.println("采购组:接收到数据类型是:transType="+dto.getTransType()+":仓库名称是:"+data.getLocationName()+",仓库ID是:"+data.getLocationId());
            System.out.println("开始执行采购组的业务...");
        }

    }
}
service中调用观察者(相当于触发了事件)
public class TransactionServiceImpl implements TransacationService {

    @Override
    public void sendTransactionData() {

        TransactionDataDTO source = new TransactionDataDTO(TransTypeEnum.Delivery.name(),
                new TransactionData(11856,100, "上海保养仓库"));

        RealSendTxDataPublisher publisher = new RealSendTxDataPublisher(source);

        //发送给财务组,财务自己玩自己的处理
        RecTxDataFmsObserver fmsObserver = new RecTxDataFmsObserver(publisher);

        //发送给采购组,采购自己玩自己的处理
        RecTxDataPmsObserver pmsObserver = new RecTxDataPmsObserver(publisher);

        publisher.addObserver(fmsObserver);
        publisher.addObserver(pmsObserver);

        publisher.notifyObservers();
    }
}

优点:观察者与被观察者之间是一个轻度的关联关系,体现了模块的高内聚,低耦合。因此对于观察者和被观察者来说,要进行扩展就比较简单了。被观察者并不需要认识任何一个具体的观察者。它只需要提供一个观察者共同的接口即可。提高代码的可复用性。
 
缺点:在数据源变化,观察者通知被观察者的时候,如果观察者过多,那么可能导致消息的传送失去实时性。

 

Spring中的ApplicationEvent事件,观察者模式

  • 事件源(也就是上述所属数据源)
  • 发起事件的人
  • 监听者(上述的观察者)

 

创建事件源

Spring中创建事件源需要继承类ApplicationEvent, 并且还要注意一定要声明显示构造方法为什么呢? 因为在Spring底层反射获取实例是用的构造方法生成的, 下面是我声明的一个事件源

@Getter
public class SendMqRequestEvent extends ApplicationEvent {

    private SendMqRequest request;

    //避免外面调用使用new方法生成对象,为什么,纯粹装逼,想想spring吧
    public static SendMqRequestEvent crateEvent(Object source,SendMqRequest request) {
        return new SendMqRequestEvent(source, request);
    }

    public SendMqRequestEvent(Object source,SendMqRequest request) {
        super(source);
        this.request = request;
    }
}

监听者(上述观察者)

1.Spring中创建监听者会使用到SmartApplicationListener, 或者接口ApplicationListener, 两个类各有各的好处 , 不过这里我们使用SmartApplicationListener 类创建Listener, 这里还需要注意一点, Spring中的监听器是有顺序执行的, 所以你还需要实现排序功能可以用注解@Order,或者实现接口Ordered, 监听器中执行方法也可以使用异步执行,看情况而定

2.这里我们创建两个监听器作为案例,测试他们的排序功能, 注意SendMqRequestRunListener 中getOrder方法return的值

3.这里监听器执行的条件可通过supportsSourceType,supportsEventType 这两个类进行过滤执行,监听器可以通过事件源获取到需要处理的数据

@Service
public class SendMqRequestRunListener implements SmartApplicationListener, Ordered {
    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        return eventType == SendMqRequestEvent.class;
    }

    @Override
    public boolean supportsSourceType(Class<?> aClass) {
        return true;
    }

    @Override
    @Async
    public void onApplicationEvent(ApplicationEvent event) {
        String source = (String) event.getSource();
        //int a = 10/0;
        System.out.println("SendMqRequestRunListener我要在这里做一些处理事件处理哦, 首先获取到的数据如下所示:"+source);

        SendMqRequestEvent event1 = (SendMqRequestEvent) event;

        System.out.println("我要在这里做一些处理事件处理哦, 首先获取到的数据如下所示:"+event1.getRequest().getTransObjectId());
    }

    @Override
    public int getOrder() {
        return 2;
    }
}


@Service
public class SendMqRequestRunListener2 implements SmartApplicationListener, Ordered {
    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        return eventType == SendMqRequestEvent.class;
    }

    @Override
    public boolean supportsSourceType(Class<?> aClass) {
        return true;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        String source = (String) event.getSource();
        System.out.println("SendMqRequestRunListener2我要在这里做一些处理事件处理哦, 首先获取到的数据如下所示:"+source);

        SendMqRequestEvent event1 = (SendMqRequestEvent) event;

        System.out.println("我要在这里做一些处理事件处理哦, 首先获取到的数据如下所示:"+event1.getRequest().getTransObjectId());
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

发布事件的人

1.为什么这个我要弄到后面讲呢? 因为这个发布者和之前上述的的发布者不太一样, Spring中年的发布者需要借用ApplicationEventPublisher,或者ApplicationContext类, 不是直接把发布事件这个动作绑定给事件源的发布者

2.这里你需要准备好数据源,然后把数据源发送给监听器,监听器获取数据源之后做一定的处理

@Override
@DBMaster
public void sysFmsData(@RequestBody SendMqRequest request) {

        publisher.publishEvent(SendMqRequestEvent.crateEvent(EventSource.SyncPuLiSiTong_NotExisted,request));
        
    }
}

上述我们将 了下 Spring的事件机制是通过观察者模式实现的, 有人可能会问SpringBoot的呢? 其实也是一样的,接下来我们就讲讲SpringBoot的事件机制吧

 

SprinBoot事件,观察者模式

SpringBoot事件主要实现SpringApplicationRunListener接口

里面包含七个方法,这七个 方法也可以说是SpringBoot的这个生命周期, 如下所示,七个方法在什么时候执行这个先不讲

public class SpringBootSelfRunListener implements SpringApplicationRunListener {

    public SpringBootSelfRunListener(SpringApplication application, String[] args) {    }

    @Override
    public void starting() {  System.out.println("hellow.....................gwm");  }
    
    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {    }
    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {    }
    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {    }
    @Override
    public void started(ConfigurableApplicationContext context) {    }
    @Override
    public void running(ConfigurableApplicationContext context) {   }
    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {    }
}

工厂机制SpringFactoriesLoader加载META-INF/spring.factories
org.springframework.boot.SpringApplicationRunListener=\
com.tuhu.stockcenter.trans.inner.service.impl.SpringBootSelfRunListener
发布事件SimpleApplicationEventMulticaster

其实Spring和SpringBoot的事件机制都是用的这个方法发送事件,具体源码还需要自己看看
 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

魔道不误砍柴功

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

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

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

打赏作者

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

抵扣说明:

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

余额充值