前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >工厂模式和策略模式结合使用的案例介绍

工厂模式和策略模式结合使用的案例介绍

作者头像
用户4283147
发布2022-10-08 11:22:52
9600
发布2022-10-08 11:22:52
举报
文章被收录于专栏:对线JAVA面试

一、前言

在前面的文章中,我们有单独介绍过工厂模式和策略模式,这两种模式是实际开发中经常会用到的,今天来介绍下将两种模式结合起来使用的场景及案例,这种结合的模式也更加的常用,能帮助我们减少if-else的使用的同时,让代码逻辑也清晰简洁、扩展性高。

二、案例

我们假设如下业务场景:

在某CRM系统中,针对不同来源(电话、短信、微信)的客户需要执行各自的名单创建逻辑。

首先,我们新建一个工程,引入相关的依赖信息:

代码语言:javascript
复制
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.18</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

然后,我们新建实体类,代表客户的信息:

代码语言:javascript
复制
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Customer {

    private String name;
    private Integer age;
    private String address;

}

然后,按照策略模式的样子,我们新建一个抽象类代表公共的策略,然后分别创建手机、短信和微信来源策略:

代码语言:javascript
复制
@Service
public abstract class CommonChannelStrategy {

    /**
     * 定义公共的检查逻辑
     * @param customer 客户信息
     * @return true-检查通过
     */
    public boolean commonCheckValidate(Customer customer) {
        return !ObjectUtils.isEmpty(customer.getName())
            && !ObjectUtils.isEmpty(customer.getAge())
            && !ObjectUtils.isEmpty(customer.getAddress());
    }

    /**
     * 各个渠道特殊的检查逻辑,各自去实现
     * @param customer 客户信息
     * @return true-检查通过
     */
    public abstract boolean specificCheckValidate(Customer customer);

}
代码语言:javascript
复制
@Service
public class TelChannelStrategy extends CommonChannelStrategy{
    /**
     * 电话渠道客户检查策略:必须大于等于18岁才合法
     * @param customer 客户信息
     * @return true-合法
     */
    @Override
    public boolean specificCheckValidate(Customer customer) {
        return !ObjectUtils.isEmpty(customer) && customer.getAge() >= 18;
    }
}
代码语言:javascript
复制
@Service
public class SmsChannelStrategy extends CommonChannelStrategy{
    /**
     * 短信渠道客户检查策略:必须大于等于12岁才合法
     * @param customer 客户信息
     * @return true-合法
     */
    @Override
    public boolean specificCheckValidate(Customer customer) {
        return !ObjectUtils.isEmpty(customer) && customer.getAge() >= 12;
    }
}
代码语言:javascript
复制
@Service
public class WechatChannelStrategy extends CommonChannelStrategy{
    /**
     * 微信渠道客户检查策略:必须大于等于22岁才合法
     * @param customer 客户信息
     * @return true-合法
     */
    @Override
    public boolean specificCheckValidate(Customer customer) {
        return !ObjectUtils.isEmpty(customer) && customer.getAge() >= 22;
    }
}

这些策略如何在合适的时机使用呢?在讲策略模式的时候,我们是借助一个环境类,持有抽象策略的引用,然后初始化该环境类的时候,传进来一个具体策略对象赋值给抽象策略。

这次讲解的是整合工厂模式,使用静态工厂方法,根据入参来从内存中找到早已初始化好的具体策略对象,即枚举中的实例对象。

代码语言:javascript
复制
@AllArgsConstructor
@Getter
public enum ENUM_CUSTOMER_CHANNEL {
    /**
     * 电话
     * 短信
     * 微信
     */
    TEL_CHANNEL("TEL_CHANNEL", "电话来源", "telChannelStrategy"),
    SMS_CHANNEL("SMS_CHANNEL", "短信来源", "smsChannelStrategy"),
    WECHAT_CHANNEL("WECHAT", "微信来源", "wechatChannelStrategy");

    /**
     * 渠道编码
     */
    private final String channelCode;
    /**
     * 渠道名称
     */
    private final String channelName;
    /**
     * 渠道处理方法
     */
    private final String channelService;

}
代码语言:javascript
复制
@Slf4j
public class ChannelFactory {

    /**
     * 存放不同渠道来源名单的处理实例bean
     * telChannelService
     * smsChannelService
     * wechatChannelService
     */
    private static final Map<String, String> SERVICE_BEAN_MAP = new HashMap<>(3);

    // 系统启动时就需要将各个渠道的处理实例bean放到map中
    static {
        for (ENUM_CUSTOMER_CHANNEL channel : ENUM_CUSTOMER_CHANNEL.values()) {
            SERVICE_BEAN_MAP.put(channel.getChannelCode(), channel.getChannelService());
        }
    }

    /**
     * 定义静态工厂方法
     *
     * @param channelCode 渠道编码
     * @return CommonChannelStrategy 具体的处理该渠道客户的实例bean
     */
    public static CommonChannelStrategy getChannelStrategy(String channelCode) {
        String beanName = SERVICE_BEAN_MAP.get(channelCode);
        if (ObjectUtils.isEmpty(beanName)) {
            log.error("渠道类型:{}错误,请检查!", channelCode);
            return null;
        }
        return (CommonChannelStrategy) SpringBeanUtils.getBean(beanName);
    }

}
代码语言:javascript
复制
@Component
public class SpringBeanUtils implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if(SpringBeanUtils.applicationContext == null) {
            SpringBeanUtils.applicationContext = applicationContext;
        }
    }

    //获取applicationContext
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    //通过name获取Bean
    public static Object getBean(String name){
        return getApplicationContext().getBean(name);
    }

    //通过class获取Bean
    public static <T> T getBean(Class<T> clazz){
        return getApplicationContext().getBean(clazz);
    }

    //通过name,以及Clazz返回指定的Bean
    public static <T> T getBean(String name,Class<T> clazz){
        return getApplicationContext().getBean(name, clazz);
    }
}

如此,我们的策略模式就和静态工厂方法模式整合好了,我们写一个单元测试试一下:

代码语言:javascript
复制
import com.example.aopdemo.AopDemoApplication;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.ObjectUtils;

@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = AopDemoApplication.class)
public class ChannelFactoryTest {

    @Test
    public void getChannelStrategy() {
        Customer customer = getCustomer();
        CommonChannelStrategy channelStrategy = ChannelFactory.getChannelStrategy("TEL_CHANNEL");
        if(ObjectUtils.isEmpty(channelStrategy)){
            log.info("渠道不合法,无法为{}提供服务!", customer.getName());
        }
        if(!channelStrategy.commonCheckValidate(customer) || !channelStrategy.specificCheckValidate(customer)){
            log.info("该客户{}电话渠道不合法!", customer.getName());
        }
        log.info("该客户{}电话渠道合法!", customer.getName());
    }

    private Customer getCustomer(){
        return Customer.builder()
            .name("张三")
            .age(19)
            .address("上海市")
            .build();
    }
}

运行后成功执行了电话渠道客户策略的行为。

三、总结

为什么要使用这种策略模式和静态工厂方法模式结合的方案呢?

  • 即减少了if-else代码;
  • 可扩展性高了;
  • 避免了自己new对象;
  • 不需要环境类以及新建环境类对象;
  • 大部分复杂业务场景的系统都会选择使用这种方案,比较成熟。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-04-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 对线JAVA面试 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、前言
  • 二、案例
  • 三、总结
相关产品与服务
短信
腾讯云短信(Short Message Service,SMS)可为广大企业级用户提供稳定可靠,安全合规的短信触达服务。用户可快速接入,调用 API / SDK 或者通过控制台即可发送,支持发送验证码、通知类短信和营销短信。国内验证短信秒级触达,99%到达率;国际/港澳台短信覆盖全球200+国家/地区,全球多服务站点,稳定可靠。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档