倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

アノテーションでデザインパターンの役割をマークする (8):「Iterator パターンの役割マーク」改善の試み 其ノ参

今回の改善は、型に付加するアノテーションにパラメータを定義して、協調する役割(型)や関係のある役割(型)を指定できるようにすることです。

例えば 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 を付加しない。
  • メソッドに付加するアノテーション
    • 型を定義するインターフェース、抽象クラスに宣言するメソッドに付加するアノテーションには @Inherited を付加する。
    • 具象クラスが実装もしくは継承によって定義を強制されているメソッドに対しては、アノテーションは付加しない(@Override を付加する)
  • フィールドに付加するアノテーション
    • 具象クラスに定義するフィールドに対しては、デザインパターンに載っている名前そのものだと型名と衝突しそうな場合、@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() {...}
}

*1:GoF デザインパターンに対するアノテーションを作っていて思ったんですが、例として用いた Iterator パターンは「アノテーションデザインパターンの役割をマークする」のに最も不適当なデザインパターンだった気がします。

*2:列挙する型を固定するのは、現実にはあまりしないでしょう。 普通 Generics を使うかな。