アノテーションは難しい
Javaのアノテーションはインタフェース扱いでクラスとして扱えない、継承ができない等、Java言語の型としては不完全なため、扱いに苦労する。また、いろいろな書き方ができるため、自己流になりがちだ。
何日か前のエントリでも書いたが、やりがちなのはアノテーションの種類が増えることを嫌がって、複数の論理的な意味を一つのアノテーションに詰め込もうとすることだ。
-
- Constraint.java
public @interface Constraint { String type() default "String"; boolean notNull() default false; int min() default 0; int max() default Integer.MAX_VALUE; String regexp(); }
このようにどんどん意味を詰め込んで行きがち(というか自分がそう)だが、これはよほど注意して設計された場合以外はお奨めできない。(フルアノテーション)
アノテーションはメタデータでありタグであるため、自己記述的であるべきである。また、アノテーションが保持する属性はできるだけ少ないほうが好ましい。可能であれば0であるべき。(マーカーアノテーション)
ということで、上のバリデーション制約のアノテーションは、今の私ならば以下のように4つに分割する。
public @interface NotNull { } public @interface DataType { Strig value() default "String"; } public @interface Length { int min() default 0; int max() default Integer.MAX_VALUE; } public @interface Pattern { Strig[] value(); }
最初に分割したNotNullアノテーションはそれを記述したエレメントに対して「非Null」の制約を課す典型的なマーカブル・アノテーションだが、分割したことで属性が不要になった訳だ。
仮にこのアノテーションをフィールドに適用するとすれば、以下のように最も多い場合4つのアノテーションが付加される。
@NotNull
@DataType("String")
@Length(min=1, max=10)
@Pattern("[A-Za-z0-9]")
Object field1;
ちょっとごちゃごちゃして見えるだろうか。
ちなみに元々のConstraintアノテーションではこうなる。
@Constraint(type="String", notNull=true, min=1, max=10, regexp="[A-Za-z0-9]")
Object field1;
一見こちらの方がすっきり見えるのだが、
分割した側もこのように書けばそう変わらない。
@NotNull @DataType("String") @Length(min=1, max=10) @Pattern("[A-Za-z0-9]")
Object field1;
変更に強く保守性に優れているのはもちろん分割した方だ。