策略+工厂模式解决过多If Else

419 阅读3分钟

1、过多使用 If Else 的不足

在日常的项目开发中,可能有这么一种场景,例如,有一个产品接口ProductService,业务中具体的产品如水果产品、电子产品等等去实现这个接口,重写不同的方法,在业务中对这块的调用,需要通过一个类型参数去获取到ProductService的不同实现类,得到不同产品。如果业务中使用IF else去根据类型参数获取不同的是实现类,随着业务的快速发展,最终的代码会变成如下:

/**
 * @author HW
 * 产品相关Controller
 */
@RestController
@RequestMapping("/product")
public class ProductController {

    /**
     * 水果产品Service组件
     */
    @Resource
    private ProductService fruitsProductService;
    
    /**
     * 电子产品Service组件
     */
    @Resource
    private ProductService electronicsProductService;
    
    ......(更多,略)

    @GetMapping("/buyProduct")
    public String buyProduct(@RequestParam("productType")String productType){
          
        String product = "";
        if ("fruitsProductService".equals(productType)) {
            product = fruitsProductService.orderingProduct();
        } else if ("electronicsProductService".equals(type)) {
            product = electronicsProductService.orderingProduct();
        }else if ("..".equals(type)) {
            product = electronicsProductService.orderingProduct();
        }else if ("..".equals(type)) {
            product = electronicsProductService.orderingProduct();
        }else if ("..".equals(type)) {
            product = electronicsProductService.orderingProduct();
        }else if ("..".equals(type)) {
            product = electronicsProductService.orderingProduct();
        }else if ("..".equals(type)) {
            product = electronicsProductService.orderingProduct();
        }
        ......(更多else if,略)
        return StringUtils.isEmpty(product) ? "购买产品失败...." : product;
    }
}

过多的 If Else,让人一眼看下去有点密集恐惧症,并且不容易维护。

那么问题来了,如何解决这种不足?我们先来看看工厂+策略模式适用场景:

“工厂+策略模式” 比较适合通过某些参数,去执行不同的业务逻辑的这种场景,能很好的解决执行逻辑相同,调用上游接口逻辑不同这种情形。这不就正是我们需要的解决方案。

springboot-ef-else-1002.png

2、Spring Boot+工厂+策略模式实战

2.1、项目依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.2.0</version>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
        <version>2.5.6</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.1.20</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

2.2、配置文件application.yml

server:
  port: 8082

2.3、产品接口类 (即策略接口类)

产品的接口类,该类就相当于策略模式中的接口类,里面定义一个策略的方法定义,子类会实现该策略方法。不过不同的是,这里接口类中定义的一种产品购买的方法,不同的子类来实现不同产品订购的方法。

/**
 * 产品相关Service接口
 * @author HW
 */
public interface ProductService {

    /**
     * 购买产品
     * @return 返回结果
     */
    String buyProduct();
}

水果产品实现类:

/**
 * 水果产品实现类
 * @author HW
 */
@Service("fruitsProductService")
public class FruitsProductServiceImpl implements ProductService {

    /**
     * 购买水果相关的产品
     * @return 返回结果
     */
    @Override
    public String buyProduct() {
        System.out.println("fruitsProductService is success.....");
        return "fruitsProductService is success.....";
    }
}

电子产品实现类:

/**
 * 电子相关产品实现了
 * @author HW
 */
@Service("electronicsProductService")
public class ElectronicsProductServiceImpl implements ProductService {

    /**
     * 购买电子相关产品
     * @return 返回结果
     */
    @Override
    public String buyProduct() {
        System.out.println("electronicsProductService is success.......");
        return "electronicsProductService is success.......";
    }
}

创建工厂类:

/**
 * @author HW
 * 产品工厂
 */
@Component
public class ProductStrategyFactory {

    /**
     * 使用依赖注入引入ProductService产品实现类,
     * 以 Bean名称作为 Map的 Key,以Bean实现类作为 Value
     */
    @Resource
    private Map<String, ProductService> strategyMap = new ConcurrentHashMap<>();

    /**
     * 查找对应的产品的处理策略
     *
     * @param productName 产品名称
     * @return 对应的产品购买逻辑实现策略
     */
    public ProductService getProductStrategy(String productName) {
        System.out.println(strategyMap.toString());
        // 根据从 productName 从 strategyMap 集合中查询对应的产品下单策略
        return strategyMap.get(productName);
    }
}

产品Controller类:

获取不同的产品下单实现类,然后调用不同的产品下单实现类去执行下单购买的业务逻辑。

/**
 * @author HW
 * 产品相关Controller
 */
@RestController
@RequestMapping("/product")
public class ProductController {

    @Resource
    private ProductStrategyFactory productStrategyFactory;

    @GetMapping("/buyProduct")
    public String buyProduct(@RequestParam("productType")String productType){
    
        ProductService productStrategy = productStrategyFactory.getProductStrategy(productType);
        String product = "";
        if (Objects.nonNull(productStrategy)){
            product = productStrategy.buyProduct();
        }

        return StringUtils.isEmpty(product) ? "购买产品失败...." : product;
    }
}

2.4、SpringBoot 启动类

/**
 * 启动类
 * @author HW
 */
@MapperScan({"com.example.mapper"})
@SpringBootApplication
public class DynamicDataSourceApplication {

    public static void main(String[] args) {
        SpringApplication.run(DynamicDataSourceApplication.class,args);
    }
}

3、案例测试

请求:http://localhost:8082/product/buyProduct?productType=electronicsProductService

结果:

Snipaste_2022-10-13_15-43-43.png

请求:http://localhost:8082/product/buyProduct?productType=fruitsProductService

结果:

Snipaste_2022-10-13_15-46-30.png