山崎屋の技術メモ

IT業界で働く中でテクノロジーを愛するSIerのシステムエンジニア👨‍💻 | AndroidとWebアプリの二刀流🧙‍♂️ | コードの裏にあるストーリーを綴るブログ執筆者✍️ | 日々進化するデジタル世界で学び続ける探究者🚀 | #TechLover #CodeArtisan、気になること、メモしておきたいことを書いていきます。

Effective Java 項目1 コンストラクタの変わりに static ファクトリーメソッドを検討する

Effective Java を再度読み直して感想や考察を書いています。

今回は、項目1「コンストラクタの変わりに static ファクトリーメソッドを検討する」です。

static ファクトリーメソッドを使用する際のメリットが 4 つ、デメリットが 2 つ紹介されています。

static ファクトリーメソッドのメリット

その1 名前を持つことができる

BigInteger クラスを例に紹介されています。

BigInteger クラスの「おそらく素数である値を作成する」コンストラクタがあります。シグネチャは次のようになっています。

public BigInteger(int bitLength, int certainty, Random rnd)

このシグネチャをみて「おそらく素数である値を作成する」と直感的には理解できません。


そして、Java 1.4 から次のシグネチャを持つ static ファクトリーメソッドが追加されました。

public static BigInteger probablePrime(int bitLength, Random rnd)

メソッド名を通じて、どのような値を返すのか推測できます。

なっとくですね。

その2 新たなオブジェクトを生成する必要がない

シングルトンの例が分かりやすいです。

何度メソッドが呼ばれても、常に返すオブジェクトは同一のもの(a == b)とするなど、オブジェクトの生成を管理できます。

インスタンスを 3 つだけ作成して使いまわすというような制御もできます。

これを「インスタンス制御されている」と言うらしい。

その3 戻り値型のサブタイプのオブジェクトを返すことができる

ちょっと分かりにくいので例を作りました。

public class Animal {
	
	// Animal の子クラスのインスタンスを返すことができる。
	public static Animal get() {
		return new Dog();
	}
	
	private static class Dog extends Animal{
		// 省略
	}
	
}

Animal クラスの利用例。

	public static void main(String[] args) {
		Animal animal = Animal.get();
		
		// 何かの処理
	}

Animal クラスの get メソッドは、シグネチャ上 Animal のインスタンスを返すことになっています。

だけど、この例のようにその子クラスの Dog を返すこともできます。しかも Dog はプライベートクラスであり、Animal の利用者からは見えません。

なにがうれしいのかというと、Dog の処理を高速化した FirstDog というクラスが発明された場合、Animal の get メソッドの利用者には気づかれずに返すオブジェクトを変更できます。

public class Animal {
	
	// Animal の子クラスのインスタンスを返すことができる。
	public static Animal get() {
		return new FirstDog();
	}
	
	private static class Dog extends Animal{
		// 省略
	}
	
	// Dog クラスの処理を高速化したもの
	private static class FirstDog extends Animal{
		// 省略
	}
	
}

利用者側のソースは変更ありません。

あるクラスとその利用者が上手いこと疎結合になります。

その4 ジェネリクスを利用したインスタンスの生成が楽になる

これは Java 6 までのお話です。

以前はジェネリクスを用いた型のインスタンス生成は次のようなものでした。

Map<Integer, String> map = new HashMap<Integer, String>();

「<Integer, String>」が 2 回登場していて冗長です。

これが、static ファクトリーメソッドを使うと次のようにすっきりするということです。

Map<Integer, String> map = SomeClass.getInstance();

Java 7 以降では次のように宣言できるので、このメリットは現在失われています。

Map<Integer, String> map = new HashMap<>();

static ファクトリーメソッドのデメリット

つぎにデメリットとして紹介されている内容です。

デメリットその1 サブクラスが作れない場合がある

static ファクトリーメソッドを使用するということは、直接 new を使用したインスタンス生成は禁止すると考えられます。

従って、コンストラクタを private にするため、そのサブクラスを作ることができません。

もっとも最近では「継承より委譲」といわれており、むしろ好ましい場面が多そうです。

継承の例。ClassB は ClassA を継承しています。

public class ClassA {
	public void method1() {

	}
}

public class ClassB extends ClassA {
	@Override
	public void method1() {
		super.method1();
		//
		// 追加したい処理。
		//
	}
}


委譲の例。ClassB は ClassA に処理を委譲している(ゆだねている)。

public class ClassA {
	public void method1() {

	}
}

public class ClassB {
	ClassA c= new ClassA();
	public void method1() {
		c.method1();
		//
		// 追加したい処理。
		//
	}
}

デメリットその2 他の static メソッドと区別がつきにくい

コンストラクタは JavaDoc でも他のメソッドとは区別されて表示されます。

一方、static ファクトリーメソッドは、他のメソッドと区別されないので埋もれやすいということです。

static ファクトリーメソッド用のマーカーアノテーションを作って、JavaDoc で強調表示するような仕様が追加になれば解決できそうですね。

まとめ

生成するインスタンスを制御できるというのが static ファクトリーメソッドのポイントです。たいしたデメリットも無いので、積極的に利用していこうと思います。

EFFECTIVE JAVA 第2版 (The Java Series)

EFFECTIVE JAVA 第2版 (The Java Series)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

  • 作者: Dustin Boswell,Trevor Foucher,須藤功平,角征典
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2012/06/23
  • メディア: 単行本(ソフトカバー)
  • 購入: 68人 クリック: 1,802回
  • この商品を含むブログ (140件) を見る
増補改訂版Java言語で学ぶデザインパターン入門

増補改訂版Java言語で学ぶデザインパターン入門