zoukankan      html  css  js  c++  java
  • 用SpringBoot实现策略模式

    问题的提出

    阅读别人代码的时候最讨厌遇到的就是大段大段的if-else分支语句,一般来说读到下面的时候就忘了上面在判断什么了。很多资料上都会讲到使用策略模式来改进这种代码逻辑。

    策略模式的类图如下:

    只需要按照这个图写代码就可以了。

    策略模式代码的实现

    借助Spring框架我们能够轻松的实现策略模式。

    举一个简单的例子,我们去咖啡店买咖啡的时候,会根据自己的喜好和胃容量选择大小杯。那么我们就要实现一个CoffeeStategy:

    package com.example.demo.strategy;
    
    public interface CoffeeStrategy {
        void offer();
    }
    

    接下来就是各种具体策略的实现了,以中杯咖啡为例:

    package com.example.demo.strategy;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Component;
    
    @Component("MID")
    @Slf4j
    public class MidCoffee implements CoffeeStrategy {
        @Override
        public void offer() {
            log.info("你的中杯咖啡");
        }
    }
    

    用Component注解给这个类起一个名字叫做MID,这个在后面的应用上下文中有起效。现在就开始定义应用上下文类:

    package com.example.demo.strategy;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.Map;
    
    @Service
    public class CoffeeContext {
        @Autowired
        private Map<String, CoffeeStrategy> coffeeStrategyMap;
    
        public void getCoffee(String size) {
            this.coffeeStrategyMap.get(size).offer();
        }
    }
    

    因为是使用了Spring框架,所有的Bean都被Spring自行管理,启动之后,Map中会有两个元素:{"MID":MidCoffee}和{"LARGE":LargeCoffee}。在具体的业务逻辑中,只需要引入应用上下文类,每次使用getCoffee方法就可以了。

    比如这个Controller方法:

    @GetMapping("/get")
        public void getCoffee(@Param("size") String size) {
            this.coffeeContext.getCoffee(size);
        }
    

    请求这个接口,我们能在后台看到具体的日志内容:

    2021-09-30 22:46:32.550  INFO 15628 --- [nio-8099-exec-1] com.example.demo.strategy.LargeCoffee    : 您的大杯咖啡
    2021-09-30 22:46:39.201  INFO 15628 --- [nio-8099-exec-7] com.example.demo.strategy.LargeCoffee    : 您的大杯咖啡
    

    进一步的思考

    之前写过Component中起的名字有奇效。如果我们没有用Spring框架去实现策略模式,那么我们的代码要如何编写呢?

    首先可以肯定的是策略接口和策略实现类是不需要变的。需要变的地方就是应用上下文了,因为不存在自动注入了。这段代码就会变成大致这样:

    package com.example.demo.strategy;
    
    public class CoffeeContext {
    
        CoffeeStrategy coffeeStrategy;
        public CoffeeContext(CoffeeStrategy coffeeStrategy) {
            this.coffeeStrategy = coffeeStrategy;
        }
    
        public void getCoffee() {
            this.coffeeStrategy.offer();
        }
    }
    

    这样,在实际使用的时候,我需要先新建一个具体的实现类对象,然后将这个对象传入策略应用上下文去。这种方式怎么看着都没有Spring的实现方式优雅。

    CoffeeStrategy mid = new MidCoffee();
    CoffeeContext context = new CoffeeContext(mid);
    context.getCoffee();
    

    在我实际改造代码的过程中我发现有些策略其实是一样的,只是个别参数不同罢了。我对接的是各个业务供应商,有些供应商的接口逻辑式样的,只是URL和USERNAME不一样罢了。于是好几个策略实现类的代码重复很严重,这个时候我使用了Java8开始提供的接口default方法。这种方法的好处就是能将这种一样的逻辑提取到interface中,只要实现类不重写,那么就会默认使用default方法。

    这样改造之后,我的代码又精简了很多。

    心得体会

    在我接手现在这个项目代码的时候,之前的程序员将代码写的很直白,就是可以不用任何的设计,直接写逻辑。这也没错,可是用IDEA的时候会各种提示重复代码啊之类的,让人看着不开心。而且还有大量的if-else分支让人摸不着头脑。

    在我大刀阔斧的改造之后,代码行数越来越少,但是可读性却越来越高。

    此时我是比较理解GoF在设计模式这本书里提到的一句话,大致意思就是开发一个面向对象的程序并不简单。

  • 相关阅读:
    k8s-pv
    k8s ---kubectl 部署时,pull image 报错,拉取不到镜像
    【knowledgebase】不要在一个很大的RDD上调用collect
    【knowledgebase】如何知道partition数
    Spark SQL External Data Sources JDBC官方实现写测试
    Spark SQL External Data Sources JDBC官方实现读测试
    Sqoop2入门之导入关系型数据库数据到HDFS上(sqoop2-1.99.4版本)
    Spark Streaming、Kafka结合Spark JDBC External DataSouces处理案例
    Spark Streaming、HDFS结合Spark JDBC External DataSouces处理案例
    Spark SQL External Data Sources JDBC简易实现
  • 原文地址:https://www.cnblogs.com/wingsless/p/15358447.html
Copyright © 2011-2022 走看看