2 回答
TA贡献1850条经验 获得超11个赞
诀窍是什么?
不要这样做。
提供静态工厂方法:
class BaseValidator<T> {
static ValidationString getValidation(String newValue) {
return new ValidationString(newValue);
}
static ValidationInteger getValidation(Integer newValue) {
return new ValidationInteger(newValue);
}
}
class ValidationString extends BaseValidator<String> { ... }
class ValidationInteger extends BaseValidator<Integer> { ... }
尽管我认为这很奇怪:您指的是基类中的子类。这种循环依赖使代码难以使用,尤其是在重构时,但也可能在初始化时。
相反,我建议创建一个实用程序类来包含工厂方法:
class Validators {
private Validators() {}
static ValidationString getValidation(String newValue) {
return new ValidationString(newValue);
}
static ValidationInteger getValidation(Integer newValue) {
return new ValidationInteger(newValue);
}
}
没有这样的循环。
关于泛型,需要意识到的一件非常重要的事情是,它只不过是使显式强制转换为隐式(然后检查所有这些隐式强制转换是否是类型安全的)。
换句话说,这:
List<String> list = new ArrayList<>();
list.add("foo");
System.out.println(list.get(0).length());
只是一种更好的写作方式:
List list = new ArrayList();
list.add((String) "foo");
System.out.println(((String) list.get(0)).length());
虽然<String>看起来它是类型的一部分,但它基本上只是对编译器的一条指令,用于喷射大量强制转换。
具有不同类型参数的泛型类都具有相同的方法。这是您的方法中的具体困难:您不能BaseValidator<String>.getValidator()用checkIsNotEmpty方法(仅)BaseValidator<Integer>.getValidator()返回某些东西,而用方法(仅)返回某些东西checkIsGreaterThan。
好吧,说你不能,这并不完全正确。在您尝试涉及方法范围的类型变量 ( <C> C getValidator()) 时,您可以编写:
new BaseValidator<>("string").<StringValidator>getValidator().checkIsNotEmpty()
(假设上面StringValidator有checkIsNotEmpty方法)
但:
让我们不要拐弯抹角:它是丑陋的。
比丑陋更糟糕的是,它不是类型安全的。你同样可以写:
新 BaseValidator<>("string").getValidator().checkIsGreaterThan(42)
这是荒谬的,但编译器允许。问题是在调用站点选择了返回类型:您要么必须返回 null (并NullPointerException在您尝试调用以下方法时得到 a );或返回一些非空值并冒险 a ClassCastException。无论哪种方式:不好。
但是,您可以做的是使通用验证器成为方法调用的参数。例如:
interface Validator<T> {
void validate(T b);
}
class BaseValidator<T> {
BaseValidator<T> validate(Validator<T> v) {
v.validate(this.value);
}
}
并像这样调用,演示如何链接方法调用以应用多个验证:
new BaseValidator<>("")
.validate(s -> !s.isEmpty())
.validate(s -> s.matches("pattern"))
...
new BaseValidator<>(123)
.validate(v -> v >= 0)
...
TA贡献1794条经验 获得超8个赞
我们决定添加更多的类步骤。您可以采用通用方式或使用显式类型的方式(在此示例中,String)。我们对所有更新方法的要求(我们有许多数据库对象......)有点复杂。我们只想要一个更新方法(对于每个数据库对象),它...
忽略字段,即为空。
忽略等于“旧”值的字段。
验证未被忽略的字段。
仅在没有发生验证问题时保存。
用许多 if 块来做到这一点是可能的,但不是真正可读的。并且复制粘贴失败的可能性很高。
我们的代码如下所示:
private void update(@NonNull final User.UpdateFinalStep params) {
UpdateWizard.update(dbUserService.get(params.getId())
.field(params.getStatus())
.withGetter(DbUser::getAccountStatus)
.withSetter(DbUser::setAccountStatus)
.finishField()
.field(Optional.ofNullable(params.getUsername())
.map(String::toLowerCase)
.orElse(null))
.withGetter(DbUser::getUsername)
.withSetter(DbUser::setUsername)
.beginValidationOfField(FieldName.USERNAME)
.notEmptyAndMatchPattern(USERNAME_PATTERN, () -> this.checkUniqueUsername(params.getUsername(), params.getId()))
.endValidation()
.field(params.getLastName())
.withGetter(DbUser::getLastname)
.withSetter(DbUser::setLastname)
.beginValidationOfField(FieldName.USER_LASTNAME)
.notEmptyAndMatchPattern(LAST_NAME_PATTERN)
.endValidation()
.field(params.getFirstName())
.withGetter(DbUser::getFirstname)
.withSetter(DbUser::setFirstname)
.beginValidationOfField(FieldName.USER_FIRSTNAME)
.notEmptyAndMatchPattern(FIRST_NAME_PATTERN)
.endValidation()
.save(dbUserService::save);
}
这是非常易读的,并且允许以非常简单的方式添加新字段。使用泛型,我们不会给“愚蠢的开发者”犯错的机会。
如图所示,accountStatus 和 username 指向不同的类。

最后,我们可以非常流畅地使用更新方法:
userService.startUpdate() .withId(currentUserId) .setStatus(AccountStatus.INACTIVE) .finallyUpdate();
添加回答
举报
