首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >重构验证器系统

重构验证器系统
EN

Code Review用户
提问于 2020-08-27 19:23:53
回答 2查看 128关注 0票数 7

我有一个验证器系统,它在保存到DB之前对输入数据进行验证。因此,假设我想要创建新用户。我们在服务班:

代码语言:javascript
复制
package main.user;

import main.entity.User;
import main.user.validator.attributesvalidators.UserAttributesValidator;
import main.user.validator.availabilityvalidators.UserAvailabilityValidator;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class UserCrudActivitiesService {

    private final UserRepository userRepository;

    public UserCrudActivitiesService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }


    public List<String> createUser(User user) {
        UserAttributesValidator userAttributesValidator = new UserAttributesValidator();
        UserAvailabilityValidator userAvailabilityValidator = new UserAvailabilityValidator(userRepository);
        List<String> messages = userAttributesValidator.validate(user);
        messages.addAll(userAvailabilityValidator.check(user));
        if (messages.isEmpty()) {
            userRepository.save(user);
            //TODO passwordencoder
        }
        return messages;
    }

    public User updateUser(User user) {
        return userRepository.save(user);
    }
}

我们有两个验证器-首先,它将检查用户的属性是否良好,然后一些属性是否是免费的,因此我们确定这个用户将是唯一的。

验证器结构:

在这两个接口中,我们得到了相同的接口:(例如,属性的接口)

代码语言:javascript
复制
package main.user.validator.attributesvalidators;

import main.entity.User;

public interface IUserAttributesValidator {

    String validate(User user);
}

然后我们得到了一些被称为UserAttributesValidator的东西,它是包含所有其他验证器的类,在它的构造函数中,我们创建了所有验证器的列表,这样我们就可以在一个流中遍历所有验证器。

代码语言:javascript
复制
package main.user.validator.attributesvalidators;

import main.entity.User;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class UserAttributesValidator {

    final private List<IUserAttributesValidator> validators;

    public UserAttributesValidator() {
        validators = new ArrayList<>();
        validators.add(new UserEmailValidator());
        validators.add(new UserFirstNameValidator());
        validators.add(new UserLastNameValidator());
        validators.add(new UserPasswordValidator());
        validators.add(new UserPhoneValidator());
        validators.add(new UsernameValidator());
    }

    public List<String> validate(User user) {
        return validators.stream()
                .map(e -> e.validate(user))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }
}

我们得到作为输出的列表,这很好。对于AvailabilityValidator也是如此

例如,关于验证器的一个:

代码语言:javascript
复制
package main.user.validator.attributesvalidators;

import main.entity.User;

public class UsernameValidator implements IUserAttributesValidator {

    public static final int NAME_MAXIMUM_LENGTH = 30;
    public static final int NAME_MINIMUM_LENGTH = 3;
    public static final String NAME_ILLEGAL_CHARACTER_REGEX = "[A-Za-z0-9]+";

    @Override
    public String validate(User user) {
        String attribute = user.getUsername();
        if (attribute.length() > NAME_MAXIMUM_LENGTH) {
            return "username is too long";
        } else if (attribute.length() < NAME_MINIMUM_LENGTH) {
            return "username is too short";
        } else if (!attribute.matches(NAME_ILLEGAL_CHARACTER_REGEX)) {
            return "username contains illegal character";
        }
        return null;
    }
}

现在,我关心的是..。我认为这是个糟糕的设计。我的意思是,我创建接口IUserAttributesValidator,然后创建包含所有其他验证器的类,这些验证器中也有方法,该方法的名称与接口中的方法相同。我想知道我能不能把这两者合并成一个?有可能吗?是否有可能改进此代码?另一个我的想法是,两者(可用性和属性检查器)都有相同的接口,有相同的方法,但不同的参数,但我不知道是否也有可能只有一个接口为两者。

EN

回答 2

Code Review用户

发布于 2020-08-28 07:52:13

正如我在下面的问题中评论的那样,java已经满足了您在将数据保存到数据库之前为数据定义自定义验证器的需要。与spring框架耦合的包是javax.validation包,因此,以您的自定义验证程序代码为例:

代码语言:javascript
复制
public class UsernameValidator implements IUserAttributesValidator {

    public static final int NAME_MAXIMUM_LENGTH = 30;
    public static final int NAME_MINIMUM_LENGTH = 3;
    public static final String NAME_ILLEGAL_CHARACTER_REGEX = "[A-Za-z0-9]+";

    @Override
    public String validate(User user) {
        String attribute = user.getUsername();
        if (attribute.length() > NAME_MAXIMUM_LENGTH) {
            return "username is too long";
        } else if (attribute.length() < NAME_MINIMUM_LENGTH) {
            return "username is too short";
        } else if (!attribute.matches(NAME_ILLEGAL_CHARACTER_REGEX)) {
            return "username contains illegal character";
        }
        return null;
    }
}

可以通过直接在User类中使用批注的机制来替换它,方法如下:

代码语言:javascript
复制
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

public class User {
    
    @Size(min=3, max=30, message="username length should be between 3 and 30 chars")
    //it seems me username should contain just these chars
    @Pattern(regexp="[A-Za-z0-9]+", message="username contains illegal characters") 
    private String username;
}

如果需要,可以将相同的机制应用于User类的其他字段。

票数 3
EN

Code Review用户

发布于 2020-08-28 08:07:38

忽略用Spring框架提供的工具实现这一点的可能性..。您所拥有的非常接近于一个复合设计模式,因此它是一个众所周知的和被接受的设计。要实现这一点,您需要更改的是让单独的验证器和复合验证器实现完全相同的接口。让IUserAttributesValidator返回一个List<String>Collection<String>是有意义的,就像在您的示例中,UsernameValidator可能会在输入中发现多个违反项,并且在同一调用中返回长度和字符设置冲突(尽管在同一个验证器中检查长度和字符集确实有点像单个责任冲突)。

另外,您可以传递一个列表作为参数,而不是返回一个列表,这样每个验证器都可以将它们的错误附加到一个现有的列表中,而不是在每个错误上创建一个新的一次性列表。

代码语言:javascript
复制
iterface Validator<T> {
    void validate(T target, List<String> errors);
}
票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/248512

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档