英文:
How to not duplicate validation code in both object and user input
问题
我是Java的初学者,正在编写我的第一个JavaFX应用程序。
我正在从用户那里接收输入,如果用户的输入有效,然后创建一个对象。
这是一个简化的代码,解释了我目前正在做的事情:
public class Visitor {
private String id;
public enum InvalidIdType {
EMPTY,
NOT_ALPHANUMERIC,
NOT_5_CHARACTERS;
}
public Visitor(String id) {
Objects.requireNonNull(id);
checkId(id);
this.id = id;
}
private void checkId(String id) {
if (validateId(id).isPresent()) {
throw new IllegalArgumentException(validateId(id).get.toString());
}
}
public static Optional<InvalidIdType> validateId(String id) {
if (id.isEmpty()) return Optional.of(InvalidIdType.EMPTY);
if (id.length != 5) return Optional.of(InvalidIdType.NOT_5_CHARACTERS);
if (...) return Optional.of(InvalidIdType.NOT_ALPHANUMERIC);
return Optional.empty();
}
}
这样在处理用户输入的其他代码中,我可以这样做:
public class Main {
public static void main(String[] args) {
...
if (Visitor.validateId(userinput).isPresent()) {
System.out.println(Visitor.validateId(userinput).get().toString())
// 告诉用户出了什么问题。我通过不同的枚举值进行切换。
}
}
}
这样,我不必在Main.java和Visitor.java中验证代码两次。
但我觉得这在Visitor.java中创建了不必要的长代码,不是Java中常见的验证输入的方式。
在对象的构造函数和处理用户输入的其他代码中验证字段的最佳实践是什么,但不重复验证代码?
英文:
I am a beginner in Java and I am writing my first application in JavaFX.
I am accepting input from a user and then creating an object if the user's input is valid.
Here's a simplified code explaining what I am currently doing:
public class Visitor {
private String id;
public enum InvalidIdType {
EMPTY,
NOT_ALPHANUMERIC,
NOT_5_CHARACTERS;
}
public Visitor(String id) {
Objects.requireNonNull(id);
checkId(id);
this.id = id;
}
private void checkId(String id) {
if (validateId(id).isPresent()) {
throw new IllegalArgumentException(validateId(id).get.toString());
}
}
public static Optional<InvalidIdType> validateId(String id) {
if (id.isEmpty()) return Optional.of(InvalidIdType.EMPTY);
if (id.length != 5) return Optional.of(InvalidIdType.NOT_5_CHARACTERS);
if (...) return Optional.of(InvalidIdType.NOT_ALPHANUMERIC);
return Optional.empty();
}
}
So that in the code elsewhere handling the user input, I can do something like:
public class Main {
public static void main(String[] args) {
...
if (Visitor.validateId(userinput).isPresent()) {
System.out.println(Visitor.validateId(userinput).get().toString())
// Tells user what is wrong. I switch-case through the different enum values.
}
}
}
So that I don't have to validate the code twice in Main.java and in Visitor.java.
But I feel like this creates unnecessarily long code in Visitor.java and is not a common way to validate inputs in Java.
What would be the best practice to validate a field in both the object's constructor and other code which handles the user input, but not duplicate my validation code.
答案1
得分: 2
将验证代码放在Visitor类内部,Visitor负责验证的责任。这可能不是您想要的。通常,您会为验证器定义一个接口:
@FunctionalInterface
public interface Validator<T> {
ValidationResult validate(T input);
}
然后,您可以拥有一系列验证器,可以通过这些验证器运行输入:
List<Validator<? super String>> idValidators = new ArrayList<>();
idValidators.add(id -> id.isEmpty() ? new ValidationResult(id, EMPTY) : ValidationResult.OK);
// ...
boolean allValid = idValidators.stream().allMatch(v -> v.validate(input) == ValidationResult.OK); // 或逐个检查错误的结果
我不确定是否应该进一步扩展这种方法,因为它相当强大并准备好执行各种不同类型的验证。这是您想要的吗?也许对您来说,本地化验证更简单、更简洁。
您真的需要在构造函数内部和外部验证输入吗?如果在外部验证,构造函数将使用有效数据进行初始化。如果在内部验证,您可以抛出异常,而不需要在外部进行验证。
无论如何,在您的主方法中两次调用了验证,这是多余的。如果要进一步处理值,可以使用Optional的方法ifPresent
或map
。
另一个建议:您想要切换枚举以报告错误。这不是很好的设计,您应该反转控制,ValidationResult应该包含消息,您只需获取它并打印它。消息可以在创建验证器时提供,或者如果要使用枚举,可以在枚举的字段中预定义。
enum ... {
EMPTY("Empty msg..."),
TOO_SHORT("Less than 5 chars"),
// ...
}
关于JavaFX的验证库,您可以在快速搜索后找到一些选项:
我不熟悉这些库,您需要进行研究。但是,我所考虑的是创建您自己的小型验证框架。
英文:
By putting the validation code inside the Visitor class, Visitor owns the responsibility of validation. This is probably not what you want. Typically, you would have an interface for a validator:
@FunctionalInterface
public interface Validator<T> {
ValidationResult validate(T input);
}
And then you have a chain of Validators, which you can run the input through:
List<Validator<? super String> idValidators = new ArrayList<>();
idValidators.add(id -> id.isEmpty ? new Result(id, EMPTY) : Result.OK);
...
idValidators.stream().allMatch(v -> v.validate(input) == OK); //or check the result one by one for the error
I am not sure if I should expand this approach further because it is quite robust and prepared to do a lot of validation of different kind. Is this what you want? Maybe localized validation is simpler and more concise for you.
Do you really need to validate the input inside the constructor and also outside? If you validate outside, the constructor is initialized with valid data. If you validate inside, you can throw and don't need to validate outside.
Anyway, in your main method you called the validation twice, which is redundant. Use the Optional's methods ifPresent
or map
if you want to process the values further
Another advice: you wanted to switch over the enum to report the errors. This is not quite a good design, you should invert the control, the ValidationResult should contain the message and you just get it and print it. The message could be provided when creating the Validator or if you want to use enums, predefined in the enum's field.
enum ... {
EMPTY("Empty msg..."),
TOO_SHORT("Less than 5 chars"),
...
}
Validation libraries for JavaFX after a quick Google:
https://dzone.com/articles/validation-javafx
https://github.com/rrohm/fx-validation
I do not know these, you need to do research. However, what I had in mind was creating your own very little validation framework.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论