所有分类
  • 所有分类
  • 未分类

观察者模式Java实战-写法1:手动注册

简介

本文介绍观察者模式最基础的写法:手动注册。

本写法的优缺点

优点

  1. 纯手写,有利于理解观察者模式的原理。

缺点

  1. 需要手动注册观察者,不能自动处理。

实例

本处使用一个贴近真实场景的案例:订单取消时,要有一系列操作,比如:账户业务需要给用户退钱、库存业务需要给商品退回数量。

在这个场景中,取消订单就是被观察者,账户业务和库存业务是观察者。

项目结构

源码地址

此隐藏内容仅限VIP查看升级VIP

业务数据类

取消订单时,主题会将这个数据给到感兴趣的观察者。

package com.knife.designPattern.bo;

import lombok.Data;

@Data
public class CancelOrderBO {
    private String orderNo;
}

主题(取消订单)

本处只使用到了被观察者的具体类,无需抽象被观察者。

package com.knife.designPattern.subject;
 

import com.knife.designPattern.bo.CancelOrderBO;
import com.knife.designPattern.observer.CancelOrderObserver;

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

/**
 * 具体被观察者(主题): 订单取消
 */
public class CancelOrderSubject {
    private final List<CancelOrderObserver> list = new ArrayList<>();

    public void addObserver(CancelOrderObserver cancelOrderObserver) {
        list.add(cancelOrderObserver);
    }
 
    public void removeObserver(CancelOrderObserver cancelOrderObserver) {
        list.remove(cancelOrderObserver);
    }
 
    public void process(CancelOrderBO cancelOrderBO) {
        System.out.println("订单取消的相关数据:" + cancelOrderBO);
        for (CancelOrderObserver cancelOrderObserver : list) {
            cancelOrderObserver.process(cancelOrderBO);
        }
    }
}

观察者

抽象观察者

package com.knife.designPattern.observer;

import com.knife.designPattern.bo.CancelOrderBO;

/**
 * 抽象观察者:业务
 */
 public interface CancelOrderObserver {
    /**
     * 业务收到订单取消数据后的操作
     */
    void process(CancelOrderBO cancelOrderBO);
}

具体观察者(账户业务) 

package com.knife.designPattern.observer.impl;

import com.knife.designPattern.bo.CancelOrderBO;
import com.knife.designPattern.observer.CancelOrderObserver;

/**
 * 具体观察者: 账户业务
 */
public class AccountCancelOrderObserverImpl implements CancelOrderObserver {
    @Override
    public void process(CancelOrderBO cancelOrderBO) {
        System.out.println("账户业务开始处理订单取消。接收到的消息:" + cancelOrderBO);
    }
}

具体观察者(库存业务)

package com.knife.designPattern.observer.impl;

import com.knife.designPattern.bo.CancelOrderBO;
import com.knife.designPattern.observer.CancelOrderObserver;

/**
 * 具体观察者: 库存业务
 */
public class StorageCancelOrderObserverImpl implements CancelOrderObserver {
    @Override
    public void process(CancelOrderBO cancelOrderBO) {
        System.out.println("库存业务开始处理订单取消。接收到的消息:" + cancelOrderBO);
    }
}

主题持有者

只有主题还不行,需要有一个地方去调用它,添加一个主题持有者

package com.knife.designPattern.subject;

import com.knife.designPattern.bo.CancelOrderBO;
import com.knife.designPattern.observer.CancelOrderObserver;
import com.knife.designPattern.observer.impl.AccountCancelOrderObserverImpl;
import com.knife.designPattern.observer.impl.StorageCancelOrderObserverImpl;

public class SubjectHolder {
    private static final CancelOrderSubject instance = init();

    private SubjectHolder() {
    }

    private static CancelOrderSubject init() {
        // 创建被观察者
        CancelOrderSubject orderCancelSubject = new CancelOrderSubject();

        // 创建观察者
        CancelOrderObserver accountCancelOrderObserver =
                new AccountCancelOrderObserverImpl();
        CancelOrderObserver storageCancelOrderObserver =
                new StorageCancelOrderObserverImpl();

        // 添加观察者
        orderCancelSubject.addObserver(accountCancelOrderObserver);
        orderCancelSubject.addObserver(storageCancelOrderObserver);
        return orderCancelSubject;
    }

    public static void process(CancelOrderBO cancelOrderBO) {
        instance.process(cancelOrderBO);
    }
}

测试

测试类

package com.knife.designPattern.controller;

import com.knife.designPattern.bo.CancelOrderBO;
import com.knife.designPattern.subject.SubjectHolder;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Api(tags = "订单")
@RestController
@RequestMapping("order")
public class OrderController {
    @ApiOperation("取消订单")
    @PostMapping("cancel")
    public String cancel(@RequestBody CancelOrderBO cancelOrderBO) {
        SubjectHolder.process(cancelOrderBO);
        return "success";
    }
}

访问接口

启动项目后,访问Knife4j(接口文档)的地址:http://localhost:8080/doc.html

(当然,你用postman也完全可以)

结果

订单取消的相关数据:CancelOrderBO(orderNo=1234)
账户业务开始处理订单取消。接收到的消息:CancelOrderBO(orderNo=1234)
库存业务开始处理订单取消。接收到的消息:CancelOrderBO(orderNo=1234)

0

评论6

请先

  1. CancelOrderSubject 中最好把process分解成notifyObservers与execute两个函数,应该把固定方法和业务方法分开,减少耦合。 public void notifyObservers() { for (CancelOrderObserver cancelOrderObserver : list) { cancelOrderObserver.process(cancelOrderBO); } } public abstract void execute();
    星痕 2023-10-26 1
    • 减少耦合的思想是好的,但此处我感觉没这个必要。如果按照观察者模式的定义,process方法应该是通知,通知了之后监听者想做啥就做啥(执行或者不执行,执行什么逻辑都由监听者决定),观察者模式就结束了。
      自学精灵 2023-10-26 1
      • 这里确实没这个必要(因为你把取消订单和观察者模式写在一起了),但我认为应该把CancelOrderSubject再进行进一步抽象,抽象出OrderSubject类,只向下暴露出execute函数,而通知过程应该写在抽象层,由execute来具体判断是否需要调用通知方法。这样,谁需要用,谁就继承,不仅在取消订单可以使用观察者模式,在生成订单,订单判断等地方一样可以使用该模式,只需实现execute函数即可。当然你这里只是演示,只做了一个取消订单功能,是可以像之前这样写的
        星痕 2023-10-26 1
        • 是的,如果需要整合更多业务类型,可以进行适当的变形。
          自学精灵 2023-10-26 0
      • 具体怎么做呢
        tanghongbing 2024-05-07 0
        • 看完设计模式这个系列,就能理解各个设计模式的含义及实战,那时就可以自己进行适当变动。看完这个系列,这个问题就迎刃而解了。
          自学精灵 2024-05-07 0
显示验证码
没有账号?注册  忘记密码?

社交账号快速登录