我有一个关于验证方法/构造函数参数的问题。
我们说我有一个班级BankAccount
。这个类在构造函数中执行一些基本的验证(注意:这不是我如何实现这样一个类,最好使用多态和接口实现来提供不同类型的银行账户。这里是一个枚举BankAccount.Type
使用)。
BankAccount(Type type, double balance, String ownerName, String accountNumber) {
// Make sure no invalid arguments are passed.
if (type == null) {
throw new IllegalArgumentException("'type' may not be null!");
}
if (balance < type.getBalanceLimit()) {
throw new IllegalArgumentException("initial 'balance' may not be less than the accounts balance limit!");
}
if (ownerName == null || ownerName.trim().isEmpty()) {
throw new IllegalArgumentException("'ownerName' may not be null or empty!");
}
if (!type.requiresOwner()) {
throw new IllegalStateException("Cannot create an account with an owner if that account type does not require an owner!");
}
if (accountNumber == null || accountNumber.trim().isEmpty()) {
throw new IllegalArgumentException("'accountNumber' may not be null or empty!");
}
this.type = type;
this.balance = balance;
this.ownerName = ownerName;
this.accountNumber = accountNumber;
}
首先:这是验证构造函数参数的方法,还是首先设置所有字段然后调用方法validateState()来检查构造状态是否有效否则抛出异常。像这样:
BankAccount(Type type, double balance, String ownerName, String accountNumber) {
this.type = type;
this.balance = balance;
this.ownerName = ownerName;
this.accountNumber = accountNumber;
validateState();
}
上述哪种解决方案更受欢迎(在大多数情况下)?
我的问题的第二部分是关于重复验证。假设我有一个班级Bank
。这个类有一个方法createGiroAccount(Account.Type type, String ownerName)
/**
* Creates a new {@link Account} of the specified {@link Account.Type} for the given owner.
* Note that the {@link Account.Type} must be a giro account type.
*
* @param type the type of the new account which must be one of the available giro options, not null
* @param ownerName the owner´s name who owns the new account, not null or empty
*/
public void createGiroAccount(BankAccount.Type type, String ownerName) {
// Really basic validation
if (type == null || !type.isGiro()) {
throw new IllegalArgumentException("'type' may not be null and must be of type giro!");
}
if (ownerName == null || ownerName.trim().isEmpty()) {
throw new IllegalArgumentException("'ownerName' may not be null or empty");
}
String accountNumber = generateUniqueAccountNumber();
accounts.add(new BankAccount(type, ownerName, accountNumber));
}
我的问题:即使BankAccount
验证了参数/状态,是否也应该验证参数?
答案 0 :(得分:1)
这是验证构造函数参数时的方法,还是首先设置所有字段然后调用方法validateState()
如果您不想编写重复代码,可以创建custom annotation并检查参数的有效性。
我的问题的第二部分是关于重复验证。
如果您只有一个构造函数,则createGiroAccount
中不需要检查。否则你会这样做。
答案 1 :(得分:1)
在构造函数或专用方法中进行验证?
两种解决方案都没问题。
诀窍在于,如果在构造函数中添加大量验证代码,并与适当的构造函数代码混合,则可能难以维护。
顺便说一句,如果你选择使用专用方法validateState();
,你应该在对构造函数做任何事情之前调用它,它对处理代码没用,后来发现你的一个参数无效并抛出异常......
如果您想进行干净验证,可以使用javax.validation
包。见this
然后,您可以创建正确的注释,并注释构造函数参数。
BankAccount(Type type, @ValidBalance double balance, @NotEmpty String ownerName, @NotEmpty String accountNumber) {
@ValidBalance
将成为自定义验证。
答案 2 :(得分:1)
很多方法可以做到,而且没有一个“正确”。在现实生活中的系统中,你会遇到像DB连接框架和REST API这样的东西,它们假设你将使用构造函数中根本没有任何参数的类。
所以你可能拥有的interface Account
无论如何都无法进行任何验证。
interface BackAccount {
AccountType getAccountType();
BigDecimal getBalance();
Person getOwner();
}
请注意,所有者与String
的班级不同;这允许假设如果您有BankAccount
,则无需验证所有者,因为在创建该对象时会发生Person
验证。你可能有一个类似
interface Validator<T> {
boolean isValid(T toValidate);
}
这将允许您为Persons,BankAccounts等实施验证。在您的情况下,您有一个
class TypeBalanceValidator<BankAccount> implements Validator<BankAccount> {
public boolean isValid(BankAccount account) {
return account.getBalance().compareTo(getMinBalanceForType(account.getType())) > 0;
}
}
至于创建BankAccount
,除了自动从数据库中读取外,你可能有一个Service
,提供了一种工厂方法,与你的方法不同,但是有类似
interface AccountService {
BankAccount createGiroAccount(Person p);
}
为什么你甚至会将类型传递给只允许AccountType.GIRO的方法?无论如何,这是您创建BankAccount
的地方以及进行验证的地方。