【阿昌之丑陋代码优化】重复代码优化方案
阿昌 Java小菜鸡

一、前言

hi,我是阿昌,在开发的过程中,不同的人有不同的编码方式和技术阶段,经过日积月累的流逝,就会出现传闻中的“屎山”代码。
那针对“屎山“的情况,今天就提供几种重复代码的优化思路,具体问题还要看具体场景。

二、正文

1、抽取公共方法

如下代码中,在for循环中无非就是对字符串进行取大小写

1
2
3
4
5
6
7
8
9
List<String> nameList = Arrays.asList("achang", "Achang", "aChang", "aachang");
for (String name : nameList) {
String upperCase = name.toUpperCase();
System.out.println(upperCase);
}
for (String name : nameList) {
String lowerCase = name.toLowerCase();
System.out.println(lowerCase);
}

那这样子可以认为给一个值然后转变为另一个值的思路进行优化,这里可以用Function接口来优化

1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) {
List<String> nameList = Arrays.asList("achang", "Achang", "aChang", "aachang");
parse2Str(nameList, String::toUpperCase,"upperCase");
parse2Str(nameList, String::toLowerCase,"lowerCase");
}
public static void parse2Str(List<String> strList, Function<String,String> function,String type){
for (String str : strList) {
String result = function.apply(str);
System.out.println(result);
}
}

2、抽取工具类

针对相同处理的逻辑,可以抽取工具类的方案;

如下代码,进行判断是否是有效的有效字符串的逻辑;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
String content = "995931576@qq.com";
Pattern pattern = Pattern.compile("(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)])", Pattern.DOTALL);
Matcher matcher = pattern.matcher(content);
if (matcher.find()) {
String result= matcher.group();
System.out.println(result);
}


String content1 = "achang3306@163.com";
Pattern pattern2 = Pattern.compile("(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)])", Pattern.DOTALL);
Matcher matcher3 = pattern.matcher(content);
if (matcher3.find()) {
String result= matcher.group();
System.out.println(result);
}

那这里就可以帮这一段逻辑抽取成工具类,入参是待判断的邮箱,返回是布尔:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void main(String[] args) {
String content = "995931576@qq.com";
System.out.println(predicateEmail(content));
String content1 = "achang3306@163.com";
System.out.println(predicateEmail(content1));
}

public static boolean predicateEmail(String email) {
Pattern pattern = Pattern.compile("(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)])", Pattern.DOTALL);
Matcher matcher = pattern.matcher(email);
boolean result = matcher.find();
if (result) {
String str= matcher.group();
System.out.println(str);
}
return result;
}

3、模型转换DO<—>DTO

如下,代码需要将UserDO—>UserDTO模型进行转换,这里就会出现set一堆参数属性的代码冗余场景;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) {
UserDO userDO = new UserDO();
userDO.setId(1L);

UserDTO userDTO = new UserDTO();
userDO.setId(userDO.getId());
}

@Data
public static class UserDO{
private Long id;
}
@Data
public static class UserDTO{
private Long id;
}

优化方案有有很多种,这里就例句3种;

  • 采用现成的框架进行,如mapstruct

  • 采用序列化复制,如ali的fastjson,这两类都需要implements Serializable

    1
    JSON.parseObject(JSON.toJSONString(userDO),UserDTO.class)
  • 采用java反射,如beanCopy

    1
    BeanUtil.copyProperties(userDO,new UserDTO());

4、利用泛型

如下方法基本一致,但是返回的类是不一样的

1
2
3
4
5
6
public List<UserDTO> getUserDTO(List<UserDO> list){
return list.stream().map(item->new UserDTO()).collect(Collectors.toList());
}
public List<UserDO> getUserDO(List<UserDTO> list){
return list.stream().map(item->new UserDO()).collect(Collectors.toList());
}

那可以用泛型来优化这段代码:

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
// 泛型方法,用于转换列表
public static <S, T> List<T> convertList(List<S> sourceList, Class<T> targetClass) {
return sourceList.stream()
.map(item -> createInstance(targetClass))
.collect(Collectors.toList());
}

// 辅助方法,用于创建目标类型的实例
private static <T> T createInstance(Class<T> clazz) {
try {
return clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException("Failed to create instance of " + clazz.getName(), e);
}
}

public static void main(String[] args) {
List<UserDO> userDOS = ...;

// 转换 UserDO 到 UserDTO
List<UserDTO> userDTOS = convertList(userDOS, UserDTO.class);

// 转换 UserDTO 到 UserDO
List<UserDO> userDOSConvertedBack = convertList(userDTOS, UserDO.class);
}

5、利用继承

比如一个制作咖啡和奶咖啡的代码:

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
@Data
public class Coffee {
protected String name;

public Coffee(String name) {
this.name = name;
}

public void prepare() {
boilWater();
brewCoffeeGrinds();
pourInCup();
addCondiments();
}

protected void boilWater() {
System.out.println("Boiling water");
}

protected void brewCoffeeGrinds() {
System.out.println("Dripping coffee through filter");
}

protected void pourInCup() {
System.out.println("Pouring into cup");
}

protected void addCondiments() {
System.out.println("Adding nothing (regular coffee)");
}
}
@Data
public class MilkCoffee {
protected String name;

public MilkCoffee(String name) {
this.name = name;
}

public void prepare() {
boilWater();
brewCoffeeGrinds();
pourInCup();
addCondiments();
}

protected void boilWater() {
System.out.println("Boiling water");
}

protected void brewCoffeeGrinds() {
System.out.println("Dripping coffee through filter");
}

protected void pourInCup() {
System.out.println("Pouring into cup");
}

protected void addCondiments() {
System.out.println("Adding Milk");
}
}

如上,只有addCondiments这步是不一样的,其他都是一模一样,这是就可以利用抽象类来优化;

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
@Data
public abstract class Coffee {
protected String name;

public Coffee(String name) {
this.name = name;
}

public void prepare() {
boilWater();
brewCoffeeGrinds();
pourInCup();
addCondiments();
}

protected void boilWater() {
System.out.println("Boiling water");
}

protected void brewCoffeeGrinds() {
System.out.println("Dripping coffee through filter");
}

protected void pourInCup() {
System.out.println("Pouring into cup");
}

protected void addCondiments() {
System.out.println("Adding nothing (regular coffee)");
}
}

1
2
3
4
5
6
7
8
9
10
@Data
public class MilkCoffee extends Coffee {
public MilkCoffee(String name) {
super(name);
}
@Override
protected void addCondiments() {
System.out.println("Adding milk");
}
}

6、利用aop切面

如下代码,每次都要在请求进来进行判断是否有权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@GetMapping("getList/#{userId}")
public Map getList(@PathVariable String userId){
User user = getUser(userId);
if (user.getRole() != null && !user.getRole().equals("admin")){
throw new RuntimeException("没有权限");
}
return new HashMap<>();
}

@GetMapping("save")
public void save(){
User user = getUser(userId);
if (user.getRole() != null && !user.getRole().equals("admin")){
throw new RuntimeException("没有权限");
}
System.out.println("save data");
}

那这里就可以使用aop切面,进行一次拦截;

1
2
3
4
5
6
7
8
9
10
11
@GetMapping("getList/#{userId}")
@PermissionAnnotation("getList")
public Map getList(@PathVariable String userId){
return new HashMap<>();
}

@GetMapping("save")
@PermissionAnnotation("save")
public void save(){
System.out.println("save data");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component
@Aspect
public class PermissionAspect {

@Before("@annotation(com.achang.springbootmavendemo.annotation.PermissionAnnotation)")
public void predicatePermission(PermissionAnnotation permissionAnnotation) throws Throwable {
String value = permissionAnnotation.value();
//判断权限
if (User.hasPermission(value)){
throw new RuntimeException("您没有权限");
}
}

}
1
2
3
4
5
6
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionAnnotation {
String value();
}
 请作者喝咖啡