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();
添加回答
舉報