突っ走り書き

見せるほどのものでは..

クエリメソッドのための Any オブジェクトパターン

SetやListなどのコレクションを全く使わないプログラミングはほとんど有り得ません.
コレクションを使うとなれば,その中から特定の条件を満たすオブジェクトを抽出するメソッドが必要になることがあります.

このエントリでは,コレクションに対するクエリメソッドの可読性を向上できる

  • Any オブジェクトパターン

を提案します.

まずは準備

Label クラスの組をもつ Pair クラスを作成しました.

public Label {
    private String label;
}
public Pair {

    private Label first;
    private Label second;

}

Pair の集合を表す Pairs で Pairのfirstによって抽出する selectFirstOf が必要になったとします.
このとき,例えば次のような実装が考えられます.

public Pairs {

    private Set<Pair> pairs;

    // ...

    public Pairs selectFirstOf(Label first) {
        Pairs ps = new Pairs();

        for (final Pair p: pairs) {
            if (p.getFirst().equals(first)) {
                ps.add(p);
            }
        }

        return ps;
    }
}

そして selectSecondOf が必要になる

このプログラムの展望を考えると selectSecondOf も必要になりそうです.
もしかすると,first と second の両方を調べるメソッドも要求されるかもしれません.

そこで Any オブジェクトパターン

Any オブジェクトパターンで重要な役割を演じるのは,
すべての Pair オブジェクト o に対して equals(o) で true となる ANY インスタンスです.
任意のLabelを表すワイルドカードにあたります.
プログラム中に Pair.ANY と書けるように,次のように実装すると便利です.

public Label {

    public static final Label ANY = new Pair("**Label#ANY**") {
        @Override
        public boolean equals(Object o) {
            // o が Label クラスであれば true を返す
            // フィールド(Label.name)は等しくなくても良い
            return o instanceof Label;
        }
    };

    // ...
}

これで,クエリメソッドは次のように実装できます.

public Pairs {

    private Set<Pair> pairs;

    public Pairs select(Pair query) {
        Pairs ps = new Pairs();

        for (final Pair p: pairs) {
            if (query.equals(p)) {
                ps.add(p);
            }
        }

        return ps;
    }

    public Pairs select(Label x, Label y) {
        return select(new Pair(x, y));
    }
}

select メソッドは次のように使います.

public class Main {
    public static void main(String[] args) {
        final Label a = new Label("a");
        final Label b = new Label("b");

        Pairs pairs = new Pairs();
        pairs.add(new Pair(a, a));
        pairs.add(new Pair(a, b));
        pairs.add(new Pair(b, a));
        pairs.add(new Pair(b, b));

        // Pair(*, *) を検索
        pairs.select(Label.ANY, Label.ANY);
        // Pair(a, *) を検索
        pairs.select(a, Label.ANY);
        // Pair(*, a) を検索
        pairs.select(Label.ANY, a);
    }
}

結果

ワイルドカード Label.ANY の存在によって,
select メソッドの呼び出し側でコードの可読性が上がりました.
また,getter である Pair#getFirst および Pair#getSecond も削除できます.

ソースコード置き場

今回実装したコードを GitHub に置いときます.