拡張性/柔軟性を獲得する章

3.1. ファクトリーメソッド

『目的』

ファクトリーメソッドは長所と短所を理解して使いましょう。

『Before』

クラスのインスタンスを得るために通常はpublicのコンストラクタを提供します。

public class Hoge {
    /**
     * コンストラクタ。
     */
    public Hoge() {}
        :
    public int method() {
        return 0;
    }    
        :
}

利用側です。

Hoge instance = new Hoge();

Hogeクラスを継承してHogeExクラスを作ってみました。

public class HogeEx extends Hoge {
    /**
     * コンストラクタ。
     */
    public HogeEx() {}
        :
    // override-method    
    @Override
    public int methodFoo() {
        return 1;
    }    
        :
}

methodFoo()メソッドをオーバーライドしています。
Hogeクラスを継承してHogeEx2クラスも作ってみました。

public class HogeEx2 extends Hoge {
    /**
     * コンストラクタ。
     */
    public HogeEx2() {}
        :
    // override-method    
    @Override
    public int methodFoo() {
        return 2;
    }
        :
}

こちらもmethodFoo()メソッドをオーバーライドしています。
でも、ちょっとした派生の割にサブクラスをどんどん作っていて手間が掛かりますね。

『After』

ファクトリーメソッドを導入してみましょう。
ファクトリーメソッドの導入によっていくつかの効果が得られます。

「長所1.ファクトリーメソッドは名前を持つことができる」

どのようなインスタンスが生成されるかメソッド名から推測できます。
これはBigIntegerクラスです。

/* コンストラクタ。 */
BigInteger(int bitLength, int certainty, Random rnd)

/* static ファクトリメソッド。 */
public static BigInteger probablePrime(int bitLength, Random rnd)

コンストラクタ名はクラス名でなければならないので、 もし複数のコンストラクタを用意するにはオーバーロードするしかありません。 (BigIntegerには6つのコンストラクタが用意されています。)

同じ名前のコンストラクタが名でたくさん用意してあると、 利用者側からは一見してどのような物か判断がつきにくくなりますよね。 まちがったコンストラクタを呼び出してしまうかもしれません。

ファクトリーメソッドならばメソッド名に制限はないので意味がわかりやすいメソッド名をつけられます。 「probablePrime」は素数を返すファクトリーメソッドです。 「BigInteger」と素っ気ないコンストラクタ名よりは想像がつきやすいですね。

「長所2.メソッドが呼び出される毎に新たなオブジェクトが生成されない」

これはBooleanクラスです。

public final class Boolean implements java.io.Serializable {

    /** プリミティブ値 true に対応する Boolean オブジェクトです。*/
    public static final Boolean TRUE = new Boolean(true);
    /** プリミティブ値 false に対応する Boolean オブジェクトです。 */
    public static final Boolean FALSE = new Boolean(false);

    /**
     * 指定された boolean 値を表す Boolean インスタンスを返します。
     */
    public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }

予め生成したインスタンスを繰り返し使用することになりますから、 不必要にたくさんのオブジェクト生成を回避することができます。

同値のオブジェクトが頻繁に利用されてなおかつ生成のコストが高い場合は、 パフォーマンスの向上に寄与するかもしれません。

「長所3.メソッドの戻り値型のどんなサブタイプのオブジェクトも返せる」

これはCollectionsクラスにあるファクトリーメソッドの1つです。

public static <T> List<T> unmodifiableList(List<? extends T> list) {
    return (list instanceof RandomAccess ?
            new UnmodifiableRandomAccessList<>(list) :  // List 実装
            new UnmodifiableList<>(list));              // List 実装
}

これ、引数に渡されたlistがRandomAccessを実装したクラスであったら・・・ みたいなことをやっているのですが、つまりどのサブタイプのオブジェクトを返すかの選択肢が広がって柔軟性が高まります。

この例ではUnmodifiableRandomAccessListとUnmodifiableListのいずれかが 返されるのですがpublicにすることなく返せています。 つまり、実装クラス(UnmodifiableRandomAccessList、UnmodifiableList)の実体を 外部から隠蔽できているので、リリース毎に変えることもできなくないわけであり、 拡張性/柔軟性が高いといえます。

ファクトリーメソッドのよろしくない点もいくつかあります。

「短所1.public/protectedのコンストラクタを持たないクラスのサブクラスを作成することはできない」

ファクトリメソッド内部では、無名クラスの形でサブクラスを返すことはできますが、 明示的にextendsしてのサブクラスは作れません。

機能拡張するのにサブクラスが作れないことは不便に思えますが、 その裏返しとしてコンポジションによる機能拡張を促すことに繋がっています。

短所というほどではないかもしれません。

「短所2.他のstaticメソッドと区別がつかなくなる」

コンストラクタとはちがって、他のユーティリティ的に使うstaticメソッドに紛れてしまいますので目立ちません。

命名規約である程度カバーしましょう。
たとえばこのように。
 valueOf:型変換的なファクトリーメソッド
 getInstance:それ以外なファクトリーメソッド

『まとめ』

コンストラクタではなく、ファクトリーメソッドでもインスタンス生成はできます。

コンストラクタとファクトリーメソッドのどちらを使うか迷ったらコンストラクタを採用しましょう。
なぜなら、コンストラクタが普通の方法ですので。

< 前のページへ

Pagetop