如何拆分此功能以降低认知复杂性?

问题描述

我是Java的新手,这个 noob 功能一直困扰着我。我已经下载了sonarLint和lo,并且发现它在我的脸上抛出了这个问题 方法的认知复杂度不应太高 。我知道它看起来很丑陋,但是谁能指出我该如何重新格式化以具有干燥的概念并且不像SonarLint所提到的那样具有很高的复杂性。

@PostMapping("/adduser")
//  @PreAuthorize("hasRole('ADMIN')")
    public ResponseEntity<MessageResponse> registerUser(@Valid @RequestBody SignupRequest signUpRequest) {
        /*
         * This controller Creates new user based on all the entities for the user
         * 
         */
         if(dbValue)
         {
             if (repository.existsByUsername(signUpRequest.getUsername())) {
                    return ResponseEntity
                            .badRequest()
                            .body(new MessageResponse("Error: Username is already taken!"));
                }

                if (repository.existsByEmail(signUpRequest.getEmail())) {
                    return ResponseEntity
                            .badRequest()
                            .body(new MessageResponse("Error: Email is already in use!"));
                }

                // Create new user's account
                User2 user = new User2(signUpRequest.getUsername(),signUpRequest.getEmail(),signUpRequest.getCustomername(),signUpRequest.getCustomerid(),signUpRequest.getDescription(),encoder.encode(signUpRequest.getPassword()));              
                
                Set<String> strRoles = signUpRequest.getRoles();                
                Set<Role2> roles = new HashSet<>();
                Role2 e = new Role2();
                e.setName("ROLE_ADMIN");
                roles.add(e);               
                user.setRoles(roles);
                repository.save(user);      
                
         }
         
         else {
             
             if (repository.existsByUsername(signUpRequest.getUsername())) {
                    return ResponseEntity
                            .badRequest()
                            .body(new MessageResponse("Error: Username is already taken!"));
                }

                if (repository.existsByEmail(signUpRequest.getEmail())) {
                    return ResponseEntity
                            .badRequest()
                            .body(new MessageResponse("Error: Email is already in use!"));
                }

                // Create new user's account
                User1 user = new User1(signUpRequest.getUsername(),encoder.encode(signUpRequest.getPassword()));
                

                Set<String> strRoles = signUpRequest.getRoles();
                Set<Role> roles = new HashSet<>();
              
                if (strRoles == null) {
                    Role userRole = roleRepository1.findByName(URole.ROLE_USER)
                            .orElseThrow(() -> new RuntimeException("Error: Role is not found."));
                    roles.add(userRole);
                } else {
                    strRoles.forEach(role -> {
                        switch (role) {
                        case "admin":
                            Role adminRole = roleRepository1.findByName(URole.ROLE_ADMIN)
                                    .orElseThrow(() -> new RuntimeException("Error: Role is not found."));                          
                            roles.add(adminRole);

                            break;
                        
                        default:
                            Role userRole = roleRepository1.findByName(URole.ROLE_USER)
                                    .orElseThrow(() -> new RuntimeException("Error: Role is not found."));
                            roles.add(userRole);
                        }
                    });
                }
                user.setRoles(roles);
                repository.save(user);
         }      
         return ResponseEntity.ok(new MessageResponse("User Added successfully!"));     
    }   

赞赏批评和任何帮助。预先感谢。

解决方法

  1. 在控制器中,您需要使用服务而不是存储库。在此处创建用户创建方法。
  2. 使用方法创建接口UserValidator(existsByUsername,existsByEmail实现谓词) Optional check(Predicate condition,String message) 或将此方法委托给新的验证实用程序类。
  3. 您已复制,可以将其移出条件
if (repository.existsByUsername(signUpRequest.getUsername())) 
    return ResponseEntity
        .badRequest() // below,you always create a new string and a new message response,use a constant as not to clog the memory
        .body(new MessageResponse("Error: Username is already taken!"));
if (repository.existsByEmail(signUpRequest.getEmail())) 
    return ResponseEntity
        .badRequest() // here too
        .body(new MessageResponse("Error: Email is already in use!"));
  1. signUpRequest.getRoles()可能会返回一个Optional;如果为空,则返回Collections.emptyList(),而不是null
  2. 通过.valueOf()获得角色,它使.forEach()条件未使用且也切换大小写。
  3. 替换此
Role userRole = roleRepository1.findByName(URole.ROLE_USER)
   .orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(userRole);

使用

roleRepository1.findByName(URole.ROLE_USER).ifPresent(roles::add);

,

只需始终如一地应用Extract Method,它将大大改善您的代码。这样做还将帮助您发现责任并为进一步的重构铺平道路。

您可以按原样使用现有代码,然后将其拆分为较小的自描述方法,以使主要算法更易于理解。

例如

if (repository.existsByUsername(signUpRequest.getUsername())) {
    return usernameTakenError();
}
if (repository.existsByEmail(signUpRequest.getEmail())) {
    return emailUsedError();
}
user = userToSignup(signUpRequest);
userRepository.save(user);
return ResponseEntity.ok(new MessageResponse("User Added successfully!")); 

在这一点上,如何真正实现这些方法并不重要:核心算法仍然简短易懂。

请注意,我经常自动将异常转换为响应错误,这允许以声明的方式检查规则 例如

//throws if taken and a generic exception handler 
//returns and error response
assertUsernameFree(); 

还要寻找干燥的机会。例如,而不是:

roleRepository1.findByName(role).orElseThrow(() -> new RuntimeException("Error: Role is not found."));

您可能会抛出roleRepository1.roleOfName(role)findExistingByName而不是返回Optional

此外,您的代码的整个角色映射部分太复杂了,我认为其中可能存在一个错误:default切换情况下,由于USER_ROLE而添加了任何未知角色,对我来说真的没有任何意义。

我希望有更多类似的东西(可能在resolveSignupRoles函数中,甚至可能是两次分派的signUpRequest.getRoles(rolesRepo)):

Set<Role> roles = signUpRequest.getRoles().stream()
    .map(r -> URole.fromCode(r))
    .map(r -> userRepository::roleOfName).collect(Collectors.toSet());

if (roles.isEmpty()) roles.add(userRepository.roleOfName(URole.ROLE_USER));

这里有大量的重构机会,但是Extract Method易于应用,并且会立即改善您编写的所有代码。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...