专业的JAVA编程教程与资源

网站首页 > java教程 正文

观察者模式-Java实现(观察者模式java实现)

temp10 2024-11-12 12:52:17 java教程 7 ℃ 0 评论


介绍


观察者模式-Java实现(观察者模式java实现)

观察者模式的使用场景非常广泛,小到代码层面的解耦,达到架构层面的系统解耦,再或者到一些产品的设计思路,都有这种模式的影子。


现在我们常说的基于事件驱动的架构,其实也是观察者模式的一种最佳实践。当我们观察某一个对象时,对象传递出的每一个行为都被看成是一个事件,观察者通过处理每一个事件来完成自身的操作处理。


生活中也有许多观察者模式的应用,比如,汽车和红绿灯的关系,‘红灯停,绿灯行’,在这个过程中交通信号灯是汽车的观察目标,而汽车是观察者。


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


在观察者模式中发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间可以没有任何相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展。


观察者模式的别名有发布-订阅(Publish/Subscribe)模式,模型-视图(Model-View)模式、源-监听(Source-Listener)模式等。


结构图



抽象依赖于抽象,不让具体类之间产生依赖关系


案例代码


如果我们要实现一个买房摇号的程序,需要通过短信告知用户摇号结果,还需要向MQ中保存用户本次摇号的信息。


未使用观察者模式的代码如下


/**
 * @Description: 开奖服务接口
 * @author: zhuoyue
 * @since: 2024/05/14 19:40
 */
public interface LotteryService {

    /**
     * 开奖之后的业务操作
     * @param uId
     * @return
     */
    public LotteryResult lottery(String uId);
}

public class LotteryServiceImpl implements LotteryService {
    //注入摇号服务
    private DrawHouseService houseService = new DrawHouseService();
    @Override
    public LotteryResult lottery(String uId) {
        //1.摇号
        String result = houseService.lots(uId);
        //发短信
        System.out.println("发送短信通知用户ID为:"+uId+",您的摇号结果如下:"+result);
        //发送MQ消息
        System.out.println("记录用户摇号结果(MQ),用户ID:"+uId+",摇号结果:"+result);
        return new LotteryResult(uId,result,new Date());
    }
}


/**
 * @Description: 开奖结果封装类
 * @author: zhuoyue
 * @since: 2024/05/14 19:38
 */
public class LotteryResult {

    /**
     * 用户Id
     */
    private String uId;
    /**
     * 摇号信息
     */
    private String msg;
    /**
     * 业务时间
     */
    private Date dataTime;

    public LotteryResult(String uId, String msg, Date dataTime) {
        this.uId = uId;
        this.msg = msg;
        this.dataTime = dataTime;
    }

    @Override
    public String toString() {
        return "LotteryResult{" +
                "uId='" + uId + '\'' +
                ", msg='" + msg + '\'' +
                ", dataTime=" + dataTime +
                '}';
    }

    public String getuId() {
        return uId;
    }

    public void setuId(String uId) {
        this.uId = uId;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Date getDataTime() {
        return dataTime;
    }

    public void setDataTime(Date dataTime) {
        this.dataTime = dataTime;
    }
}


public class DrawHouseService {
    //摇号抽签
    public String lots(String uId){
        if(uId.hashCode() % 2 == 0){
            return "恭喜ID为:"+uId+"的用户,在本次摇号中中签!";
        }else{
            return "很遗憾,ID为:"+uId+"的用户,您本次未中签";
        }
    }
}


使用观察者模式优化后的代码


上面的摇号业务中,发短信、发MQ消息是一个顺序调用的过程,但是除了摇号这个核心功能以外,发短信与记录信息到MQ的操作都不是主链路的功能,需要单独抽取出来,这样才能保证在后面的开发过程中保证代码的可扩展性和可维护性。



/**
 * @Description: 事件监听接口
 * @author: zhuoyue
 * @since: 2024/05/14 20:04
 */
public interface EventListener {
    void doEvent(LotteryResult result);
}


/**
 * @Description: MQ发送事件监听类
 * @author: zhuoyue
 * @since: 2024/05/14 20:06
 */
public class MQEventListener implements EventListener {
    @Override
    public void doEvent(LotteryResult result) {
        System.out.println("记录用户的摇号结果(MQ),用户ID为:"+result.getuId()
                +",您的摇号结果为:"+result.getMsg());
    }
}

/**
 * @Description: 短信发送事件监听类
 * @author: zhuoyue
 * @since: 2024/05/14 20:06
 */
public class MessageEventListener implements EventListener {
    @Override
    public void doEvent(LotteryResult result) {
        System.out.println("发送短信通知,用户ID:"+result.getuId()
                +",您的摇号结果为:"+result.getMsg());
    }
}


/**
 * @Description: 事件处理类
 * @author: zhuoyue
 * @since: 2024/05/14 20:10
 */
public class EventManager {
    public enum EventType{
        MQ,Message
    }

    //监听器集合
    Map<Enum<EventType>, List<EventListener>> listeners = new HashMap<>();


    public EventManager(Enum<EventType>... operations){
        for (Enum<EventType> operation : operations) {
            this.listeners.put(operation,new ArrayList<>());
        }
    }

    /**
     * 订阅
     * @param eventType 事件类型
     * @param listener 监听对象
     */
    public void subscribe(Enum<EventType> eventType, EventListener listener){
        List<EventListener> users = listeners.get(eventType);
        users.add(listener);
    }

    /**
     * 取消订阅
     * @param eventType 事件类型
     * @param listener 监听对象
     */
    public void unsubscribe(Enum<EventType> eventType,EventListener listener){
        List<EventListener> users = listeners.get(eventType);
        users.remove(listener);
    }

    /**
     * 通知
     * @param eventType 事件类型
     * @param result 结果
     */
    public void notify(Enum<EventType> eventType, LotteryResult result){
        List<EventListener> users = listeners.get(eventType);
        for (EventListener listener : users) {
            listener.doEvent(result);
        }
    }
}


/**
 * @Description: 开奖服务接口
 * @author: zhuoyue
 * @since: 2024/05/14 19:40
 */
public abstract class LotteryService {
    private EventManager eventManager;

    public LotteryService() {
        //设置事件类型
        eventManager = new EventManager(EventManager.EventType.Message, EventManager.EventType.MQ);
        //订阅
        eventManager.subscribe(EventManager.EventType.Message,new MessageEventListener());
        eventManager.subscribe(EventManager.EventType.MQ,new MQEventListener());
    }

    public LotteryResult lotteryAndMsg(String uId){
        LotteryResult result = lottery(uId);
        //发送通知
        eventManager.notify(EventManager.EventType.Message,result);
        eventManager.notify(EventManager.EventType.MQ,result);
        return result;
    }

    /**
     * 开奖之后的业务操作
     * @param uId
     * @return
     */
    public abstract LotteryResult lottery(String uId);
}


/**
 * @Description: 开奖服务
 * @author: zhuoyue
 * @since: 2024/05/14 19:41
 */
public class LotteryServiceImpl extends LotteryService {
    //注入摇号服务
    private DrawHouseService houseService = new DrawHouseService();
    @Override
    public LotteryResult lottery(String uId) {
        //1.摇号
        String result = houseService.lots(uId);
        return new LotteryResult(uId,result,new Date());
    }
}


代码测试


@Test
    public void test02(){
        LotteryServiceImpl lotteryService = new LotteryServiceImpl();
        LotteryResult result = lotteryService.lotteryAndMsg("12345678998754632146");
        System.out.println(result);
    }


总结

  • 优点
  1. 降低了目标和观察者之间的耦合关系,两者之间是抽象耦合关系。
  2. 被观察者发送通知,所有注册的观察者都会收到信息【可以实现广播机制】。
  • 缺点
  1. 如果观察者非常多的话,那么所有的观察者收到被观察者发送的通知会耗时
  2. 如果被观察者有循环依赖的话,那么被观察者发送通知会使观察者循环调用,会导致系统崩溃
  • 使用场景
  1. 当一个对象的改变需要改变其他对象时。比如,商品库存数量发生变化时,需要通知商品详情页、购物车等系统改变数量
  2. 一个对象发生改变时只想要发送通知,而不需要知道接受者是谁。比如,订阅微信公众号的文章,发送者通过公众号发送,订阅者并不知道哪些用户订阅公众号。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表