今回の改善は、型に付加するアノテーションにパラメータを定義して、協調する役割(型)や関係のある役割(型)を指定できるようにすることです。
例えば Iterator パターンにおいて、Aggregate は Iterator を生成しますが、Iterator の役割をするインターフェースが BookIterator という独自タイプ(実装クラスではなくインターフェース)の場合に
@Aggregate(iteratorType = BookIterator.class)
のように指定できるようにしましょう、ということです*1。 いろいろなデザインパターンを一緒に使うときの識別子 (ID) と言えばいいでしょうか。 もちろん不必要なときには指定しなくても言いように、デフォルト値(Void.class にするのが妥当でしょう)を設定することにしましょう。
Iterator パターンの登場人物
以前もやりましたが、Iterator パターンで登場する型を列挙しておきます。 以前の場合と書き方を少し変えてありますが。 @Xxxx はアノテーションを、「@」がついていない xxxxType はクラスオブジェクトを引数にとるアノテーションのパラメータを表しています。
- @Aggregate → 型
- iteratorType : Class<?>
- @CreateIterator → メソッド
- @ConcreteAggregate → 具象クラス
- aggregateType : Class<?>
- @Iterator → 型
- iteratedType : Class<?>
- @HasNext → メソッド
- @Next → メソッド
- @ConcreteIterator → 具象クラス
- iteratorType : Class<?>
- @AggregateInstance → フィールド
アノテーションを定義する Java コード
上記の登場人物をアノテーションにすると以下のようになります。
package org.waman.tools.design.gof.behavioral; import java.lang.annotation.*; public final class IteratorPattern { private IteratorPattern(){} @Target(ElementType.TYPE) public static @interface Aggregate{ Class<?> iteratorType() default Void.class; @Inherited @Target(ElementType.METHOD) public static @interface CreateIterator{} } @Target(ElementType.TYPE) public static @interface ConcreteAggregate { Class<?> aggregateType() default Void.class; } @Target(ElementType.TYPE) public static @interface Iterator { Class<?> iteratedType() default Void.class; @Inherited @Target(ElementType.METHOD) public static @interface HasNext {} @Inherited @Target(ElementType.METHOD) public static @interface Next {} } @Target(ElementType.TYPE) public static @interface ConcreteIterator { Class<?> iteratorType() default Void.class; @Inherited @Target(ElementType.FIELD) public static @interface AggregateInstance{} } }
アノテーションの設計方針
アノテーションの大まかな設計方針を羅列しておきます。
- 全てのアノテーション
- @Documented, @Retention は付加する必要があるかも知れませんが、とりあえず無視。
- 型に付加するアノテーション
- 型(インターフェース、クラス)に付加するアノテーションには @Inherited を付加しない。
- メソッドに付加するアノテーション
- フィールドに付加するアノテーション
- 具象クラスに定義するフィールドに対しては、デザインパターンに載っている名前そのものだと型名と衝突しそうな場合、@XxxxInstance という名前にする。
サンプルコード in 『Java 言語で学ぶデザインパターン入門』
またまた『Java 言語で学ぶデザインパターン入門』の Iterator の章に載っているサンプルに対して、上記のアノテーションを付加してみましょう。 ただし、今回は Iterator は Generics にしておらず、列挙する型を Book に固定しています*2。 そのため、名前も Iterator → BookIterator, Aggregate → BookAggregate と変更しています。
Book クラス
これは列挙される型です。 デザインパターンでの役割はありません。
public class Book {...}
BookIterator インターフェース
Java では Iterator の役割をする型は java.util.Iterator インターフェースを実装しているのが普通でしょうが、簡単のためここではそうしていません。
import org.waman.tools.design.gof.IteratorPattern; import org.waman.tools.design.gof.IteratorPattern.Iterator.HasNext; import org.waman.tools.design.gof.IteratorPattern.Iterator.Next; @IteratorPattern.Iterator(iteratedType = Book.class) public interface BookIterator { @HasNext boolean hasNext(); @Next Book next(); }
BookShelfIterator クラス
Iterator の具象クラスです。
import org.waman.tools.design.gof.IteratorPattern; import org.waman.tools.design.gof.IteratorPattern.ConcreteIterator.HasNext; import org.waman.tools.design.gof.IteratorPattern.ConcreteIterator.Next; @IteratorPattern.ConcreteIterator(iteratorType = BookIterator) public class BookShelfIterator implements BookIterator { ... @Override public boolean hasNext() {...} @Override public Book next() {...} }
BookAggregate インターフェース
Iterator を生成するクラス。
import org.waman.tools.design.gof.IteratorPattern; import org.waman.tools.design.gof.IteratorPattern.Aggregate.CreateIterator; @IteratorPattern.Aggregate(iteratorType = BookIterator.class) public interface BookAggregate { @CreateIterator BookIterator iterator(); }
BookShelf クラス
Aggregate の具象クラス。
import org.waman.tools.design.gof.IteratorPattern; import org.waman.tools.design.gof.IteratorPattern.ConcreteAggregate.CreateIterator; @IteratorPattern.ConcreteAggregate(aggregateType = BookAggregate.class) public class BookShelf implements BookAggregate{ ... @Override public BookIterator iterator() {...} }