项目中某一个函数被代码检查工具扫出来137行,属于超大函数(大于50行的函数),经过两次重构达到自己认为的理想状态,使用到了反射,函数式接口,以及建造者模式来完成。下面案例介绍,涉及项目代码,部分简化,仅供给大家参考思想,如有更好的重构方法,欢迎留言。
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221public class SctpParaEntity extends Model<SctpParaEntity> { /** * 表ID */ public static final int TABLE_ID = 379; private static final long serialVersionUID = 6407871887365507698L; private Integer clusterId; private int maxIpNum; private int minRtoValue; private int maxRtoValue; private int initTtoValue; private int alphaRtoValue; private int betaRtoValue; private int hearteatInterval; private int maxAssocRetTimes; private int maxPathRetTimes; private int noCongestLevel; private int lowCongestLevel; private int highCongestLevel; private int chkSendSum; private int chkRecvSum; private int chkSumType; private int cookieValidTime; private int maxInitRetTimes; private int inStreamNum; private int outStreamNum; private int dispatchPacketFlag; private int sctpBundFlag; private int extendSupportFlag; private int protocolLayerType; private int ackPolicy; private int dataNum; private int delayDuration; private int sendAck; private int lifeTimeFlag; private int ciycleNum; /** * SCTP参数转换成界面显示的list * * @return the list */ public List<SctpParaDto> eto() { List<SctpParaDto> tpLst = new ArrayList<>(); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "maxIpNum", String.valueOf(maxIpNum))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "minRtoValue", String.valueOf(minRtoValue))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "maxRtoValue", String.valueOf(maxRtoValue))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "initTtoValue", String.valueOf(initTtoValue))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "alphaRtoValue", String.valueOf(alphaRtoValue))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "betaRtoValue", String.valueOf(betaRtoValue))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "hearteatInterval", String.valueOf(hearteatInterval))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "maxAssocRetTimes", String.valueOf(maxAssocRetTimes))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "maxPathRetTimes", String.valueOf(maxPathRetTimes))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "noCongestLevel", String.valueOf(noCongestLevel))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "lowCongestLevel", String.valueOf(lowCongestLevel))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "highCongestLevel", String.valueOf(highCongestLevel))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "chkSendSum", EtoMessage.changeFlagToStr(chkSendSum))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "chkRecvSum", EtoMessage.changeFlagToStr(chkRecvSum))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "chkSumType", EtoMessage.changeSumTypeToStr(chkSumType))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "cookieValidTime", String.valueOf(cookieValidTime))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "maxInitRetTimes", String.valueOf(maxInitRetTimes))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "inStreamNum", String.valueOf(inStreamNum))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "outStreamNum", String.valueOf(outStreamNum))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "dispatchPacketFlag", EtoMessage.changeFlagToStr(dispatchPacketFlag))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "sctpBundFlag", EtoMessage.changeFlagToStr(sctpBundFlag))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "extendSupportFlag", EtoMessage.changeFlagToStr(extendSupportFlag))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "protocolLayerType", EtoMessage.changeLevelTypeToStr(protocolLayerType))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "ackPolicy", EtoMessage.changeAckPolicyToStr(ackPolicy))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "dataNum", String.valueOf(dataNum))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "delayDuration", String.valueOf(delayDuration))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "sendAck", String.valueOf(sendAck))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "lifeTimeFlag", EtoMessage.changeFlagToStr(lifeTimeFlag))); tpLst.add( new SctpParaDto( CurrentRequest.getAppName(), CurrentRequest.getClusterName(), "ciycleNum", String.valueOf(ciycleNum))); return tpLst; } }
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
27public class SctpParaDto { private String appName; // 项目类型 private String clusterName; // 集群名称 private Integer clusterId; // 集群ID private String paramName; // 参数名 private String paramValue; // 参数值 /** * 无参构造函数 */ public SctpParaDto() {} /** * 有参构造函数 * * @param appName 项目名称 * @param clusterName 集群名称 * @param paramName 参数名称 * @param paramValue 参数值 */ public SctpParaDto(String appName, String clusterName, String paramName, String paramValue) { this.appName = appName; this.clusterName = clusterName; this.clusterId = CurrentRequest.getClusterId(); this.paramName = paramName; this.paramValue = paramValue; } }
这里有两个类,SctpParaEntity和SctpParaDto,一个用于数据库插入,一个用于页面展示,需要将entity转换为dtoList。这里存在的问题,eto这个方法名太随意,不能准确表达方法意思,同时由于entity字段较多,需要将其中29个字段转换,直接new对象,虽然很好理解,但是不够优雅,造成超大函数,进行了第一次重构。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20@Data @Builder public class SctpParaDto { private String appName; // 项目类型 private String clusterName; // 集群名称 private Integer clusterId; // 集群ID @NotBlank @StrEnumCheck(enumClass = SctpParaEnum.class, message = "{InvalidSctpParameters.message}") @Pattern(regexp = "^[a-zA-Z0-9]+$") private String paramName; // 参数名 @NotBlank @Pattern(regexp = "^[a-zA-Z0-9_]+$") private String paramValue; // 参数值 /** * 无参构造函数 */ @Tolerate public SctpParaDto() {} }
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
29public List<SctpParaDto> convertToSctpParaDtoList() { List<SctpParaDto> list = new ArrayList<>(29); // 初始化29个 Field[] fields = this.getClass().getDeclaredFields(); List<String> flagToStrList = Arrays.asList("chkSendSum", "chkRecvSum", "dispatchPacketFlag", "sctpBundFlag", "extendSupportFlag", "lifeTimeFlag"); SctpParaDto.SctpParaDtoBuilder builder = SctpParaDto.builder() .appName(CurrentRequest.getAppName()) .clusterName(CurrentRequest.getClusterName()) .clusterId(CurrentRequest.getClusterId()); for (int i = 3; i < fields.length; i++) { // 从第4个fieLd开始反射 String fieldName = fields[i].getName(); Object fieldValue = ReflectUtils.invokeGet(this, fieldName); builder.paramName(fieldName); if (flagToStrList.contains(fieldName)) { builder.paramValue(EtoMessage.changeFlagToStr((int) fieldValue)); } else if (Objects.equals(fieldName, "ackPolicy")) { builder.paramValue(EtoMessage.changeAckPolicyToStr((int) fieldValue)); } else if (Objects.equals(fieldName, "protocolLayerType")) { builder.paramValue(EtoMessage.changeLevelTypeToStr((int) fieldValue)); } else if (Objects.equals(fieldName, "chkSumType")) { builder.paramValue(EtoMessage.changeSumTypeToStr((int) fieldValue)); } else { builder.paramValue(String.valueOf(fieldValue)).build(); } list.add(builder.build()); } return list; }
将Dto增加@Builder注解,使用建造者模式,删除原有的构造方法,无参构造需要增加@Tolerate注解,否则会报错。Entity中的eto方法名改为convertToSctpParaDtoList,同时使用反射和建造者模式创建dto对象,大大简化了代码,但是还是存在问题。convertToSctpParaDtoList方法依旧在entity这个类中,造成类的职责过多,同时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
27
28
29
30
31
32
33
34
35
36
37
38
39
40public class SctpParaAssembler { private static final Map<String, Function<Integer, String>> SCTP_PARA_PROCESS_MAP; static { SCTP_PARA_PROCESS_MAP = new HashMap<>(); List<String> stringList = Arrays.asList("maxIpNum", "minRtoValue", "maxRtoValue", "initTtoValue", "alphaRtoValue", "betaRtoValue", "hearteatInterval", "maxAssocRetTimes", "maxPathRetTimes", "noCongestLevel", "lowCongestLevel", "highCongestLevel", "cookieValidTime", "maxInitRetTimes", "inStreamNum", "outStreamNum", "dataNum", "delayDuration", "sendAck", "ciycleNum"); stringList.forEach(str -> SCTP_PARA_PROCESS_MAP.put(str, String::valueOf)); List<String> flagToStrList = Arrays.asList("chkSendSum", "chkRecvSum", "dispatchPacketFlag", "sctpBundFlag", "extendSupportFlag", "lifeTimeFlag"); flagToStrList.forEach(str -> SCTP_PARA_PROCESS_MAP.put(str, EtoMessage::changeFlagToStr)); SCTP_PARA_PROCESS_MAP.put("ackPolicy", EtoMessage::changeAckPolicyToStr); SCTP_PARA_PROCESS_MAP.put("protocolLayerType", EtoMessage::changeLevelTypeToStr); SCTP_PARA_PROCESS_MAP.put("chkSumType", EtoMessage::changeSumTypeToStr); } /** * Convert to sctp para dto list list * * @param entity entity * @return the list */ public static List<SctpParaDto> convertToSctpParaDtoList(SctpParaEntity entity) { List<SctpParaDto> list = new ArrayList<>(29); // 初始化29个 SctpParaDto.SctpParaDtoBuilder builder = SctpParaDto.builder() .appName(CurrentRequest.getAppName()) .clusterName(CurrentRequest.getClusterName()) .clusterId(CurrentRequest.getClusterId()); Field[] fields = entity.getClass().getDeclaredFields(); for (int i = 3; i < fields.length; i++) { // 从第4个fieLd开始反射 String fieldName = fields[i].getName(); Integer fieldValue = (Integer) ReflectUtils.invokeGet(entity, fieldName); list.add(builder.paramName(fieldName) .paramValue(SCTP_PARA_PROCESS_MAP.get(fieldName).apply(fieldValue)) .build()); } return list; } }
将上述方法单独抽取出来放到SctpParaAssembler 中,使用Function接口对每个字段处理,Map去获取Function,消除了if else,利于扩展,同时让类的职责更加明确。
最后
以上就是拉长煎蛋最近收集整理的关于Java超大函数代码重构案例分析——使用反射、函数式接口Function、建造者模式Builder重构的全部内容,更多相关Java超大函数代码重构案例分析——使用反射、函数式接口Function、建造者模式Builder重构内容请搜索靠谱客的其他文章。
发表评论 取消回复