重构的那些事儿

Posted 苦糖?

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了重构的那些事儿相关的知识,希望对你有一定的参考价值。


if/else的恶瘤

有句话说的好–好文章是改出来,同样,好的代码也肯定是重构出来的,因为没有哪个软件工程师能够拍着胸脯保证在项目之初代码设计这块,就考虑到了所有需求变化可能性的扩展。随着项目的不断成长,业务逻辑变的越来越复杂,代码也开始变的越来越多,原有的设计可能不再满足需求,那么此时必须要重构。就系统整体架构而言,重构可能需要很大的改动,可能在架构流程上需要评审;就功能内代码层次而言,这种重构在我们编码过程中随时可以进行,类似于if/else,swicth/case这种代码的重构也属于这种类型。今天我们要重构的if/else源码如下所示,针对不同的status code,CountRecoder对象会执行不同的set方法,为不同内部属性赋值。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19


​public​​​ ​​ CountRecoder getCountRecoder(List countEntries) ​

​CountRecoder countRecoder = ​​​ ​​new​​​ ​​ CountRecoder();​

​for​​​ ​​ (CountEntry countEntry : countEntries) ​

​if​​​ ​​ (​​​ ​​1​​​ ​​ == countEntry.getCode()) ​

​countRecoder.setCountOfFirstStage(countEntry.getCount());​

​ ​​​ ​​else​​​ ​​ if​​​ ​​ (​​​ ​​2​​​ ​​ == countEntry.getCode()) ​

​countRecoder.setCountOfSecondStage(countEntry.getCount());​

​ ​​​ ​​else​​​ ​​ if​​​ ​​ (​​​ ​​3​​​ ​​ == countEntry.getCode()) ​

​countRecoder.setCountOfThirdtage(countEntry.getCount());​

​ ​​​ ​​else​​​ ​​ if​​​ ​​ (​​​ ​​4​​​ ​​ == countEntry.getCode()) ​

​countRecoder.setCountOfForthtage(countEntry.getCount());​

​ ​​​ ​​else​​​ ​​ if​​​ ​​ (​​​ ​​5​​​ ​​ == countEntry.getCode()) ​

​countRecoder.setCountOfFirthStage(countEntry.getCount());​

​ ​​​ ​​else​​​ ​​ if​​​ ​​ (​​​ ​​6​​​ ​​ == countEntry.getCode()) ​

​countRecoder.setCountOfSixthStage(countEntry.getCount());​

​​

​​

​return​​​ ​​ countRecoder;​

​​


CountRecoder对象是一个简单的Java Bean,用于保存一天之中六种状态分别对应的数据条目,提供了get和set方法。CountEntry是对应数据库中每种状态的数据条目记录,包含状态code和以及count两个字段, 我们可以使用mybatis实现数据库记录和java对象之间的转换。上面getCountRecoder的方法实现了将list转换为CountRecoder的功能。

看到这段代码,想必已经有很多人要呵呵了,像一坨啥啥啥,长得这么丑,真不知道它”爸妈”怎么想的,怎么敢”生”出来。啥都不说了,直接回炉重构吧。重构是门艺术,Martin flow曾写过一本书《重构改变代码之道》,里面详细的记录了重构的方法论,感兴趣的朋友可以阅读一下。说到重构,通常我们在重构中会遇到一个问题,那就是如何能够保证重构的代码不改变原有的外部功能特征 ?经过TDD训练的朋友应该知道答案,那就是单元测试,重构之前要写单元测试,准确的来说应该是补单元测试,毕竟TDD的核心理念是测试驱动开发。对于今天博客中分享的例子,因为代码逻辑比较简单,所以偷了懒,省却了单元测试的历程。

重构初体验–反射

要重构上面的代码,对设计模式精通的人可以立马可以看出来这是使用策略模式/状态模式的绝佳场景,将策略模式稍微变换,工厂模式应该也是ok的,当然也有些人会选择使用反射。对于这些方法,这里不一一列出,主要想讲一下使用反射和工厂模式如何解决消除if/else问题,那先说反射吧,代码如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26


​private​​​ ​​ static​​​ ​​ Map methodsMap = ​​​ ​​new​​​ ​​ HashMap();​

 

​static​​​ ​​ ​

​methodsMap.put(​​​ ​​1​​​ ​​, ​​​ ​​"setCountOfFirstStage"​​​ ​​);​

​methodsMap.put(​​​ ​​2​​​ ​​, ​​​ ​​"setCountOfSecondStage"​​​ ​​);​

​methodsMap.put(​​​ ​​3​​​ ​​, ​​​ ​​"setCountOfThirdtage"​​​ ​​);​

​methodsMap.put(​​​ ​​4​​​ ​​, ​​​ ​​"setCountOfForthtage"​​​ ​​);​

​methodsMap.put(​​​ ​​5​​​ ​​, ​​​ ​​"setCountOfFirthStage"​​​ ​​);​

​methodsMap.put(​​​ ​​6​​​ ​​, ​​​ ​​"setCountOfSixthStage"​​​ ​​);​

​​

 

​public​​​ ​​ CountRecoder getCountRecoderByReflect(List countEntries) ​

​CountRecoder countRecoder = ​​​ ​​new​​​ ​​ CountRecoder();​

​countEntries.stream().forEach(countEntry -> fillCount(countRecoder, countEntry));​

​return​​​ ​​ countRecoder;​

​​

 

​private​​​ ​​ void​​​ ​​ fillCount(CountRecoder shippingOrderCountDto, CountEntry countEntry) ​

​String name = methodsMap.get(countEntry.getCode());​

​try​​​ ​​ ​

​Method declaredMethod = CountRecoder.​​​ ​​class​​​ ​​.getMethod(name, Integer.​​​ ​​class​​​ ​​);​

​declaredMethod.invoke(shippingOrderCountDto, countEntry.getCount());​

​ ​​​ ​​catch​​​ ​​ (Exception e) ​

​System.out.println(e);​

​​

​​


重构初体验–所谓模式

使用反射去掉if/else的原理很简单,使用HashMap建立状态码和需要调用的方法的方法名之间的映射关系,对于每个CountEntry,首先取出状态码,然后根据状态码获得相应的要调用方法的方法名,然后使用java的反射机制就可以实现对应方法的调用了。本例中使用反射的确可以帮助我们完美的去掉if/else的身影,但是,众所周知,反射效率很低,在高并发的条件下,反射绝对不是一个良好的选择。除去反射这种方法,能想到的就剩下使用策略模式或者与其类似的状态模式,以及工厂模式了,我们以工厂模式为例,经典的架构UML架构图通常由三个组成要素:

  1. 抽象产品角色:通常是一个抽象类或者接口,里面定义了抽象方法
  2. 具体产品角色:具体产品的实现类,继承或是实现抽象策略类,通常由一个或多个组成类组成。
  3. 工厂角色:持有抽象产品类的引用,负责动态运行时产品的选择和构建

策略模式的架构图和工厂模式非常类似,不过在策略模式里执行的对象不叫产品,叫策略。在本例中,这里的产品是虚拟产品,它是服务类性质的接口或者实现。Ok,按照工厂模式的思路重构我们的代码,我们首先定义一个抽象产品接口FillCountService,里面定义产品的行为方法fillCount,代码如下所示:

1

2

3


​public​​​ ​​ interface​​​ ​​ FillCountService ​

​void​​​ ​​ fillCount(CountRecoder countRecoder, ​​​ ​​int​​​ ​​ count);​

​​



​Java设计模式之工厂模式分析【简单工厂、工厂方法、抽象工厂】​

pdf

重构的那些事儿_ide

0星超过10%的资源67KB

重构的那些事儿_策略模式_02


​下载​

接着我们需要分别实现这六种服务类型的产品,在每种产品中封装不同的服务算法,具体的代码如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41


​class​​​ ​​ FirstStageService ​​​ ​​implements​​​ ​​ FillCountService ​

​@Override​

​public​​​ ​​ void​​​ ​​ fillCount(CountRecoder countRecoder, ​​​ ​​int​​​ ​​ count) ​

​countRecoder.setCountOfFirstStage(count);​

​​

​​

 

​class​​​ ​​ SecondStageService ​​​ ​​implements​​​ ​​ FillCountService ​

​@Override​

​public​​​ ​​ void​​​ ​​ fillCount(CountRecoder countRecoder, ​​​ ​​int​​​ ​​ count) ​

​countRecoder.setCountOfSecondStage(count);​

​​

​​

 

​class​​​ ​​ ThirdStageService ​​​ ​​implements​​​ ​​ FillCountService ​

​@Override​

​public​​​ ​​ void​​​ ​​ fillCount(CountRecoder countRecoder, ​​​ ​​int​​​ ​​ count) ​

​countRecoder.setCountOfThirdtage(count);​

​​

​​

 

​class​​​ ​​ ForthStageService ​​​ ​​implements​​​ ​​ FillCountService ​

​@Override​

​public​​​ ​​ void​​​ ​​ fillCount(CountRecoder countRecoder, ​​​ ​​int​​​ ​​ count) ​

​countRecoder.setCountOfForthtage(count);​

​​

​​

 

​class​​​ ​​ FirthStageService ​​​ ​​implements​​​ ​​ FillCountService ​

​@Override​

​public​​​ ​​ void​​​ ​​ fillCount(CountRecoder countRecoder, ​​​ ​​int​​​ ​​ count) ​

​countRecoder.setCountOfFirthStage(count);​

​​

​​

 

​class​​​ ​​ SixthStageService ​​​ ​​implements​​​ ​​ FillCountService ​

​@Override​

​public​​​ ​​ void​​​ ​​ fillCount(CountRecoder countRecoder, ​​​ ​​int​​​ ​​ count) ​

​countRecoder.setCountOfSixthStage(count);​

​​

​​


紧接着,我们需要是实现工厂角色,在工厂内需要实现产品的动态选择算法,使用HashMap维护状态code和具体产品的对象之间的映射关系,就可以非常容易的实现这一点,具体代码如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17


​public​​​

以上是关于重构的那些事儿的主要内容,如果未能解决你的问题,请参考以下文章

网站重构那些事儿

Android+Handler+Thread 那些事儿

浅谈react 那些事儿~

反编译那些事儿—枚举的反编译

有关dubbo面试的那些事儿

黑金原创教程 FPGA那些事儿 — 概念篇

(c)2006-2024 SYSTEM All Rights Reserved IT常识