フレームワークとうまく統合できるかどうかはわかりませんが、次のことを提案したいと思います。
- 検証ルールを実装するクラスを受け取るアノテーションを作成します
- アノテーションが受け取れるインターフェースを作成する
- ルールのロジックを持つインターフェイスの実装を作成します
- モデル クラスに注釈を追加する
- 注釈付きフィールドごとに検証を適用する注釈プロセッサを作成する
次の例は Groovy で作成しましたが、標準の Java ライブラリと慣用的な Java を使用しています。読めないものがあれば警告する:
import java.lang.annotation.*
// Our Rule interface
interface Rule<T> { boolean isValid(T t) }
// Here is the annotation which can receive a Rule class
@Retention(RetentionPolicy.RUNTIME)
@interface Validation { Class<? extends Rule> value() }
// An implementation of our Rule, in this case, for a Person's name
class NameRule implements Rule<Person> {
PersonDAO dao = new PersonDAO()
boolean isValid(Person person) {
Integer mode = dao.getNameValidationMode()
if (mode == 1) { // Don't hardcode numbers; use enums
return person.name ==~ "[A-Z]{1}[a-z ]{2,25}" // regex matching
} else if (mode == 2) {
return person.name ==~ "[a-zA-Z]{1,25}"
}
}
}
これらの宣言の後、使用法:
// Our model with an annotated field
class Person {
@Validation(NameRule.class)
String name
}
// Here we are mocking a database select to get the rule save in the database
// Don't use hardcoded numbers, stick to a enum or anything else
class PersonDAO { Integer getNameValidationMode() { return 1 } }
注釈の処理:
// Here we get each annotation and process it against the object
class AnnotationProcessor {
String validate(Person person) {
def annotatedFields = person.class.declaredFields.findAll { it.annotations.size() > 0 }
for (field in annotatedFields) {
for (annotation in field.annotations) {
Rule rule = annotation.value().newInstance()
if (! rule.isValid(person)) {
return "Error: name is not valid"
}
else {
return "Valid"
}
}
}
}
}
そしてテスト:
// These two must pass
assert new AnnotationProcessor().validate(
new Person(name: "spongebob squarepants") ) == "Error: name is not valid"
assert new AnnotationProcessor().validate(
new Person(name: "John doe") ) == "Valid"
また、GContractsを見てください。これは、注釈による興味深い検証モデルを提供します。