概述
项目中某一个函数被代码检查工具扫出来137行,属于超大函数(大于50行的函数),经过两次重构达到自己认为的理想状态,使用到了反射,函数式接口,以及建造者模式来完成。下面案例介绍,涉及项目代码,部分简化,仅供给大家参考思想,如有更好的重构方法,欢迎留言。
public 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;
}
}
public 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对象,虽然很好理解,但是不够优雅,造成超大函数,进行了第一次重构。
@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() {}
}
public 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分支过多,不利于扩展,于是进行第二次重构。
public 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重构所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复