有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

SpringJava语义有没有更好的编写方法?

我正在构建一个Spring后端。我有一个控制器,它会得到一个“搜索对象”——一个有10个字段的对象,其中只有一个字段需要填充,所以搜索函数(我没有编写,但需要进行更改和重构)是这样编写的:

if( param1 != null ) user = getUserByParam1(param1);
else if ( param2 != null ) user = getUserByParam2(param2);
.
.
.
else if(lastName != null || lastName != null) user = getUserByName(firstName, lastName);
else user = getUserById(id);

if(user == null) throw costumException;
return user;

请注意最后的两个特殊情况——其中一个检查两个参数的可用性,而不是一个,并将它们发送到同一个函数(该函数可以在其中一个字段中处理null,但不能同时处理这两个字段),以及默认情况,该情况假定传递了一个ID(如果不是,则由if (user == null)检查后抛出异常)

有没有办法重构这段代码,使其更具可读性/美观?有没有什么设计模式或已知的方法可以用来实现这一点?或者,这实际上是编写这种功能的最佳方式吗

我想了很多,但找不到更好的方法。我想出了一个主意,将填充的字段名及其值发送到另一个函数,该函数将在字段名上“切换大小写”,并将值发送到相应的函数,但它并没有真正节省太多代码(因为我仍然需要手动迭代所有字段以找到已填充的字段),而且我不确定它是否更具可读性

我对Java也很陌生,所以我不知道所有可用的API和接口,也许你可以向我提供一些帮助


共 (3) 个答案

  1. # 1 楼答案

    根据访问数据的方式,可以使用getByExample查询。您有一个将它here用于Spring数据的示例。这样,您只需从api获取用户类(必须从api接收搜索查询的对象,该对象必须与用户对象类似,因为参数看起来很相似)

    然后从api中传递用户搜索对象,并在存储库中执行getByExample操作

    user = getByExample(userSearchObject)
    

    但这都是假设您在api中只收到一个填充在对象中的字段。这将是最简洁的方法

    否则,我建议将所有参数包装到api中的一个类中,如下所示:

    public class UserSearchQuery() {
        private String param1;
        private String param2;
        ...
        
        // getters + setters
    }
    

    然后使用criteria query和criteria query builders(一篇关于它的文章here

    在您的服务中,您只需执行以下操作:

    public class UserService() {
    
    private UserRepository repository;
       public User search(UserSearchQuery query) {
          return repository.search(query);
       }
    }
    

    在回购协议中:

    public class UserRepository() {
       private EntityManager em;
       
       public User search(UserSearchQuery query) {
          CriteriaBuilder builder = em.getCriteriaBuilder();
          CriteriaQuery<User> query = builder.createQuery(User.class);
    
          Root<User> user = query.from(User.class);
          List<Predicate> predicates = new ArrayList<>();
         
          if (query.getParam1() != null) {
              predicates.add(builder.equal(user.get("param1"), query.getParam1()));
          }
          ...
          // and the other ones goes here
       }
    }
    

    这样,您的用户将只有一个搜索方法,而不再有10个方法,每个参数一个,要添加搜索条件,您只需向UserSearchQuery类添加一个新参数并添加一个新谓词。此外,如果有一天你希望能够使用多个参数作为搜索条件,这将已经完成

  2. # 2 楼答案

    Note: This is simply a workaround for your quite monstrous method. As mentioned in the comments, your endpoint tries to do too much. It would be much better to create many different endpoints which all only need the required parameters. Makes it a lot easier to understand than just to see 10+ if-else statements


    您可以创建一个包含所有可能参数的数据-class

    public class UserRequest {
        private String lastName;
        private String firstName;
    
        // more fields, constructors, getters, setters etc.
    }
    

    然后让interface看起来像这样:

    public interface UserRequestResolver {
        User resolve(UserRequest request);
    }
    

    然后,可以根据给定的参数(如果存在)来实现该接口。返回找到的User或简单地null

    下一步是,创建一个List<UserRequestResolver> resolvers,并添加不同的实现,使用Java8+可以使用lambdas:

    resolvers.add(r -> r.getParam1() != null ? getUserByParam1(r.getParam1()) : null);
    resolvers.add(r -> {
        if(r.getFirstName() == null && r.getLastName()) return null;
        return getUserByName(r.getFirstName(), r.getLastName());
    });
    // etc.
    

    然后,当收到一个UserRequest时,你可以简单地在resolvers上迭代,并获取第一个不是null的返回值:

    for(UserRequestResolver resolver : resolvers) {
        User user = resolver.resolve(request);
        if(user != null) return user;
    }
    throw costumException;
    

    如果您是Stream的粉丝,那么可以用以下内容替换上面的for循环:

    return resolvers.stream()
        .map(resolver -> resolver.resolve(request))
        .filter(Objects::nonNull)
        .findFirst()
        .orElseThrow(() -> costumException);