代码优化之争-SpringUtil与策略模式

     背景

       又到了一周一度的代码审查环节,作为团队里的老咸鱼,我需要对新同事近一周编写的代码进行检查,并指出他们的不足,督促改正。近期同事小明负责研发的功能是根据ID查询同一类对象的详细信息,这些对象有着相似的结构和属性,相同的父类,所以其查询逻辑实现起来也很简单,一个接口就可以搞定,但是小明的代码却总能给我带来惊喜,如下:

@RestController
@RequestMapping("/original")
public class OriginalController {
    @Autowired
    private ServiceA serviceA;

    @Autowired
    private ServiceB serviceB;

    @Autowired
    private ServiceC serviceC;

    @Autowired
    private ServiceD serviceD;

    @Autowired
    private ServiceE serviceE;

    @GetMapping("/getDataById/{id}")
    public BasicInfo getDataById(@RequestParam(value ="type") String type, @PathVariable("id") String id) {
        Assert.notEmpty(type, "类型不能为空");
        if (type.equals("A")) {
            return serviceA.getDataById(id);
        }
        if (type.equals("B")) {
            return serviceB.getDataById(id);
        }
        if (type.equals("C")) {
            return serviceC.getDataById(id);
        }
        if (type.equals("D")) {
            return serviceD.getDataById(id);
        }
        if (type.equals("E")) {
            return serviceE.getDataById(id);
        }
        return new BasicInfo(id);
    }
}

       看完代码,我把小明拉过来就是一通猛批:“看看你写的啥玩意儿?谁让你这么写代码的?公司让你们学设计模式,你就是这么学习致用的?赶紧给我改啰!”

     工厂模式/策略模式

        没过一会儿,小明找我说:“雷哥啊,我改完了,这回我不仅用了设计模式,还用了两种模式实现,你给看看?”

        我看了新代码,发现他还真是用了两种模式:“工厂模式”和“策略模式”,代码如下:

// 工厂服务类
@Service
public class FactoryServiceA {
    @Autowired
    private ServiceA serviceA;

    @Autowired
    private ServiceB serviceB;

    @Autowired
    private ServiceC serviceC;

    @Autowired
    private ServiceD serviceD;

    @Autowired
    private ServiceE serviceE;

    public BasicService<? extends BasicInfo> getServiceByType(String type) {
        switch (type) {
            case "A" : return serviceA;
            case "B" : return serviceB;
            case "C" : return serviceC;
            case "D" : return serviceD;
            case "E" : return serviceE;
        }
        return null;
    }
}

//Controller方法
    @Autowired
    private FactoryServiceA factoryServiceA;

    @GetMapping("/getDataByIdA/{id}")
    public BasicInfo getDataByIdA(@RequestParam(value ="type") String type, @PathVariable("id") String id) {
        Assert.notEmpty(type, "类型不能为空");
        BasicService<? extends BasicInfo> service = factoryServiceA.getServiceByType(type);
        if (service != null) {
            return service.getDataById(id);
        }
        return new BasicInfo(id);
    }

// 策略模式服务类
@Service
public class StrategyService {

    @Autowired
    private ApplicationContext applicationContext;

    private BasicService<? extends BasicInfo> service;

    public void fillServiceByType(String type) {
        switch (type) {
            case "A":
                service = applicationContext.getBean(ServiceA.class);
                break;
            case "B":
                service = applicationContext.getBean(ServiceB.class);
                break;
            case "C":
                service = applicationContext.getBean(ServiceC.class);
                break;
            case "D":
                service = applicationContext.getBean(ServiceD.class);
                break;
            case "E":
                service = applicationContext.getBean(ServiceE.class);
                break;
            default:
                service = null;
                break;
        }
    }

    public BasicInfo getDataById(String id) {
        if (service != null) {
            return service.getDataById(id);
        }
        return new BasicInfo(id);
    }
}

// Controller方法
    @Autowired
    private StrategyService strategyService;

    @GetMapping("/getDataById/{id}")
    public BasicInfo getDataById(@RequestParam(value ="type") String type, @PathVariable("id") String id) {
        Assert.notEmpty(type, "类型不能为空");
        strategyService.setService(type);
        return strategyService.getDataById(id);
    }

       能在短短的半小时内,用两种模式重构原来的功能,说明小明的能力还是挺不错的。我感到很欣慰,毕竟他是在我的指导下才有这番成长的。同时我也有点心虚,我承认我刚才批他的时候声音有点大。

        看代码可以发现,在这个方法里,工厂模式和策略模式其实在结构上是比较相似的,区别在于封装的维度,比如把实现方法的每个service作为一个对象封装,通过一个类来选择对应的service,就是工厂模式,如果把service和它的方法作为一个整体来封装,通过一个类来执行对应的方法,就是策略模式。根据迪迷特法则(一个对象应当对其他对象有尽可能少的了解),很明显策略模式更加高明,因为在策略模式里,Controller根本不需要知道BasicService类其及方法。

        但是他明明用一种模式实现就行了,为什么要写两种?他是不是在挑衅我?我感觉他就是在挑衅我!

     用Map代替Switch

       上面的实现方式虽然不错,但也并不是毫无破绽。以我多年有CV经验,没一会就发现了问题,Switch!对,正经人谁用switch啊?这玩意分支多了会增加代码的圈复杂度,case里的条件只能用常量,新手写起来容易漏break,易出错,写起来不优雅,不易扩展,早就被大流淘汰了。于是我准备在新人面前露一手,用Map代替Switch,对工厂服务进行如下调整:

@Service
public class FactoryServiceB {

    @Autowired
    private ServiceA serviceA;

    @Autowired
    private ServiceB serviceB;

    @Autowired
    private ServiceC serviceC;

    @Autowired
    private ServiceD serviceD;

    @Autowired
    private ServiceE serviceE;

    private final Map<String, BasicService> serviceMap = new HashMap<>();

    @PostConstruct
    private void fillDataIntoMap() {
        serviceMap.put("A", serviceA);
        serviceMap.put("B", serviceB);
        serviceMap.put("C", serviceC);
        serviceMap.put("D", serviceD);
        serviceMap.put("E", serviceE);
    }


    public BasicService getServiceByType(String type) {
        return serviceMap.get(type);
    }

       通过map取service的方式,比case更加简洁,而且有效的解决了扩展难的问题,如果以后需要添加新对象F,直接把service引入,在Map里加一行就行了。

       老前辈毕竟还是老前辈啊,就在我沾沾自喜时,部门的胡大佬”恰巧“路过,看了我的代码当场脸色铁青:“这谁写的代码?谁让他这么写的?这种代码不是在丢咱们团队的脸吗?”

        我有点方:“大佬,这个代码,有,有什么问题吗?”

        胡大佬义正严辞的说:“正经人谁在代码里用魔法值啊,这ABCDE为什么不建个枚举?而且你这代码也太臃肿了,我要是想增加一个对象,扩展起来多不方便?”

     工具包的妙用

       胡大佬丝毫不给我狡辩的机会,拽过键盘就是一顿输出,分分钟建好了枚举类,还优化了之前的策略模式:

// 枚举类
public enum ServiceTypeEnum {
    TYPE_A("A", ServiceA.class),
    TYPE_B("B", ServiceB.class),
    TYPE_C("C", ServiceC.class),
    TYPE_D("D", ServiceD.class),
    TYPE_E("E", ServiceE.class),
    ;

    public String getType() {
        return type;
    }

    public Class<? extends BasicService> getServiceClass() {
        return serviceClass;
    }

    private final String type;

    private final Class serviceClass;


    ServiceTypeEnum(String type, Class serviceClass) {
        this.type = type;
        this.serviceClass = serviceClass;

    }

    public static ServiceTypeEnum getEnumByType(String type) {
        ServiceTypeEnum[] values = ServiceTypeEnum.values();
        for (ServiceTypeEnum en : values) {
            if (en.getType().equals(type)) {
                return en;
            }
        }
        return null;
    }


// 改造后的StrategyService 
@Service
public class StrategyServiceB {
    private BasicService<? extends BasicInfo> service;

    public void setService(String type) {
        service = SpringUtil.getBean(ServiceTypeEnum.getEnumByType(type).getServiceClass());
    }

    public BasicInfo getDataById(String id) {
        if (service != null) {
            return service.getDataById(id);
        }
        return new BasicInfo(id);
    }
}


// 引入maven依赖
        <!-- 工具类 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.12</version>
        </dependency>

        这个代码论简洁度,规范度完爆了我跟小明的版本,而且扩展起来十分方便,添加新对象时,只需要在枚举类里加一行即可。看完代码,小明情不自禁的献上了自己的膝盖,但是,我不胡。虽然我被碾压,被按在地上磨擦,可我就是不胡!抛开技术不谈,胡大佬难道就没有问题么?

     技术的背后

      (警告,后面一部分纯属胡说八道了,研发人员千万不要看!)

       我承认胡大佬比我强一丢丢,但也只是一丢丢而己,他怎么可能随手一段代码就完爆我?而且我的工位在角落里,他没事跑过来干啥?时间还掐得那么精准?这一切真的是“恰巧”吗?

       这么一想,小明也有问题啊!他明明会用设计模式,为什么要把代码写成这样?而且小明与胡大佬的关系也很值得推敲,胡大佬恰巧是小明的导师。

       现在看来,这段代码更像是一个设计出来的陷阱,专等着某个傻子踩进去。情势犹如拨云见日,逐渐明朗起来,我有理由怀疑,这是针对我的一次有预谋的狙击。他俩串通好设下这个局,就是想在大庭广众之下用我最擅长的东西狠狠打我的脸。我们什么仇什么怨,他俩要这样害我?

       这个问题我一直没找着答案,直到在整理这篇博客时,我忽然发现之前写的一篇博客的阅读数变成了2。这就奇怪了,我写的东西除了我,还有谁会看?凝视着那满是嘲讽意味的阅读数,我想我知道什么原因了,某人肯定已经知道我在博客上黑他的事了。

       程序员的世界不仅是简简单单的COPY,还有办公室里波云诡谲,连横合纵的明争暗斗。呵呵呵,事情,开始变得有趣了呢!

 

  • 14
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值