Spring项目中策略模式实战

avatar 2020年08月28日01:13:04 0 277 views

在公司的项目中,在某些场景使用设计模式来完成自己的需求,很能体现一个人的代码水平。

本文介绍策略模式,在 Spring 项目(或SpringBoot) 中引入策略模式来解决一个通过不同的方式通知用户的场景。

比如用户注册、忘记密码或其他推送信息,我们需要给用户发送信息,可以通过电子邮件、电话、微信、短信等方式通知用户。

 

比较 low 的代码如下:

public void notice(String noticeWay, String userId) {
        if ("EMAIL".equals(noticeWay)) {
            // TODO 通过电子邮件通知用户具体逻辑
            System.out.println("通过电子邮件通知用户具体逻辑");
            
        } else if ("PHONE".equals(noticeWay)) {
            // TODO 通过电话通知用户具体逻辑
            System.out.println("通过电话通知用户具体逻辑");
            
        } else if ("SMS".equals(noticeWay)) {
            // TODO 通过短信通知用户具体逻辑
            System.out.println("通过短信通知用户具体逻辑");
            
        } else if ("WECHAT".equals(noticeWay)) {
            // TODO 通过微信通知用户具体逻辑
            System.out.println("通过微信通知用户具体逻辑");
            
        } 
        // ...
    }

这样的代码,每新增一个通知策略,需要加一个 else if,然后再里面写具体的逻辑。

代码显得很臃肿,难以维护,不易于扩展。

然后,我们开始介绍使用策略模式来解决这个问题。

 

废话不多说,直接上代码

代码如下

1、策略工厂

通知策略工厂,用于获取具体通知策略Bean对象,比如微信通知策略对象,邮件策略通知对象

因为我们使用 Spring 管理Bean,所以我们可以把通知方式和Bean对象维护在一个 Map 里

package com.liuyanzhao.sens.demo.factory;

import com.liuyanzhao.sens.demo.strategey.NoticeStrategy;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

/**
 * 通知策略工厂
 *
 * @author 言曌
 * @date 2020/8/28 12:17 上午
 */
@Service
public class NoticeStrategyFactory {

    /**
     * 存储通知方式和策略Bean关系
     */
    public static Map<String, NoticeStrategy> STRATEGY_MAP = new HashMap<>();

    /**
     * 注册策略Bean
     *
     * @param noticeWay      通知方式
     * @param noticeStrategy 策略Bean
     */
    public static void registerBean(String noticeWay, NoticeStrategy noticeStrategy) {
        STRATEGY_MAP.put(noticeWay, noticeStrategy);
    }

    /**
     * 获取策略Bean
     *
     * @param noticeWay 通知方式
     * @return
     */
    public NoticeStrategy getNoticeStrategyBean(String noticeWay) {
        return STRATEGY_MAP.get(noticeWay);
    }
    
}

 

2、策略接口

通知策略接口,目前只有一个通知接口方法

package com.liuyanzhao.sens.demo.strategey;

import com.liuyanzhao.sens.demo.dto.NoticeStrategyDTO;

/**
 * @author 言曌
 * @date 2020/8/28 12:20 上午
 */

public interface NoticeStrategy {

    /**
     * 通知用户
     *
     * @param strategyDTO
     */
    void notice(NoticeStrategyDTO strategyDTO);
    
}

 

3、策略DTO

即入参对象封装,里面很重要的属性就是通知方式 noticeWay ,其他字段属于业务字段

package com.liuyanzhao.sens.demo.dto;

import lombok.Data;

/**
 * 通知策略DTO
 * @author 言曌
 * @date 2020/8/28 12:21 上午
 */
@Data
public class NoticeStrategyDTO {

    /**
     * 通知方式: EMAIL、SMS、PHONE、WECHAT
     */
    private String noticeWay;

    /**
     * 用户ID
     */
    private String userId;

    // other
    
}

 

4、上下文 Context

用于提供给调用方直接调用,符合开闭原则

即其他代码,如 controller 层需要调用通知用户 notice 方法,直接调用 Context 类的 notice 方法

package com.liuyanzhao.sens.demo.context;

import com.liuyanzhao.sens.demo.dto.NoticeStrategyDTO;
import com.liuyanzhao.sens.demo.strategey.NoticeStrategy;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author 言曌
 * @date 2020/8/28 12:40 上午
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class NoticeStrategyContext {

    /**
     * 注入策略类:通过构造器方式
     */
    private NoticeStrategy noticeStrategy;


    /**
     * 通知方法
     *
     * @param strategyDTO
     */
    public void notice(NoticeStrategyDTO strategyDTO) {
        noticeStrategy.notice(strategyDTO);
    }
    


}

 

5、具体的策略实现

(1)电子邮件通知策略

package com.liuyanzhao.sens.demo.strategey;

import com.liuyanzhao.sens.demo.dto.NoticeStrategyDTO;
import com.liuyanzhao.sens.demo.factory.NoticeStrategyFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;

/**
 * 电子邮件通知策略
 *
 * @author 言曌
 * @date 2020/8/28 12:24 上午
 */
@Service
public class EmailNoticeStrategy implements NoticeStrategy, InitializingBean {

    private static final String noticeWay = "EMAIL";

    /**
     * 项目启动时,初始化当前类的Bean时调用
     */
    @Override
    public void afterPropertiesSet() {
        NoticeStrategyFactory.registerBean(noticeWay, this);
    }

    @Override
    public void notice(NoticeStrategyDTO strategyDTO) {
        // TODO 通过电子邮件通知用户具体逻辑
        System.out.println("通过电子邮件通知用户具体逻辑");
    }
}

(2)微信通知策略

package com.liuyanzhao.sens.demo.strategey;

import com.liuyanzhao.sens.demo.dto.NoticeStrategyDTO;
import com.liuyanzhao.sens.demo.factory.NoticeStrategyFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;

/**
 * 微信通知策略
 *
 * @author 言曌
 * @date 2020/8/28 12:25 上午
 */
@Service
public class WechatNoticeStrategy implements NoticeStrategy, InitializingBean {

    private static final String noticeWay = "WECHAT";

    /**
     * 项目启动时,初始化当前类的Bean时调用
     */
    @Override
    public void afterPropertiesSet() {
        NoticeStrategyFactory.registerBean(noticeWay, this);
    }

    @Override
    public void notice(NoticeStrategyDTO strategyDTO) {
        // TODO 通过微信通知用户具体逻辑
        System.out.println("通过微信通知用户具体逻辑");
    }
}

 

 

其他的策略没必要贴了,道理都是一样的,需要在策略类Bean初始化时,将Bean放到工厂类的 Map 里即可

这样的好处是不需要单独维护 Bean 和 noticeWay 的关系

以后我们新增一个通知策略,比如 QQ 通知用户,只需要新增一个 QQNoticeStrategy 类即可,然后在这个类里指定具体的 noticeWay 即可

 

  • 微信
  • 交流学习,有偿服务
  • weinxin
  • 博客/Java交流群
  • 资源分享,问题解决,技术交流。群号:590480292
  • weinxin
avatar

发表评论

avatar 登录者:匿名
您需要登录才能评论,可以选择注册或者QQ快速登录

     

已通过评论:0   待审核评论数:0