よく使うJavaライブラリで味わうデザインパターン - Factoryパターン


普段よく使うJavaライブラリにも、デザインパターンが隠されています。日々の作業が忙しく見逃しがちですが、たまにはじっくり一種の芸術ともいえる美しい設計を味わってみましょう。

今回の芸術

import java.net.URL;
import java.net.URLConnection;
...

URL url = new URL("http:で始まる文字列");
URLConnection connection = url.openConnection();

引数で指定したURLに接続するオブジェクトを生成するシーンですが、あらためて鑑賞してみると、神秘的なインターフェイスに見入ってしまいます。

鑑賞のポイント

URLConnectionインスタンスを生成するために、new URLConnection(url); とするのではなく、わざわざURLクラスを介しています。しかし、それでもプログラマーに違和感を持たせないシンプルさに美しさを感じます。設計者の想いを探ってみましょう。

Factoryパターンを使わない場合

それではまず、最も直感的なコードで、HTTPで接続するためのオブジェクトを生成してみましょう。

// 注意: コンストラクタがprotectedなので実際はこのようには書けない
URLConnection connection = new HttpURLConnection(url);

接続方法にHTTPしか想定していない場合は、この方法で問題ないと思います。しかし、今後、HTTP以外のXXXプロトコルでも接続する必要が出てきた場合、HttpURLConnectionXXXURLConnectionどちらかを意識して書き分ける必要が出てきます。

URLConnection connection = new HttpURLConnection(url);

とか、

URLConnection connection = new XXXURLConnection(url);

というような、複数種類のインスタンス生成コードがプロジェクトのいろいろな個所に書かれることになります。

また、もし使用するクラスをHttpURLConnectionからXXXURLConnectionに統一することになった場合、すべての該当するコードを修正しなくてはいけません。これは美しくありませんね。

実は実際に、URLConnectionのサブクラスには、JarURLConnectionというものもあります。(このクラスはローカル上のjarファイルにアクセスすることを想定しています1。)

Factoryパターンを使った場合

冒頭のコードでは、Factoryパターンが使われています。

URLのコンストラクタにhttp:で始まる文字列をセットした場合は、url.openConnection(); で、HttpURLConnectionクラスのインスタンスが返ります。

URL url = new URL("http:で始まる文字列"); // Factoryクラス
HttpURLConnection connection = (HttpURLConnection)url.openConnection(); // HttpURLConnectionにキャストできる

そして、jar:で始まる文字列をセットした場合は、JarURLConnectionクラスのインスタンスが返ります。

URL url = new URL("jar:で始まる文字列"); // Factoryクラス
JarURLConnection connection = (JarURLConnection)url.openConnection(); // JarURLConnectionにキャストできる

Factoryパターンを使えば、どのクラスのインスタンスを生成するかをFactoryクラスに任せることができるのです。さらに、インスタンスをnewするコードは隠蔽されるため、同じインターフェイスのまま、内部の実装を変更することもできます。なんてエレガントなのでしょう!

Factoryパターンへの専門家のコメント

多くの専門家からも、Factoryパターンを評価するコメントが寄せられています。

Yoshihara Hidehikoさん

オブジェクトの使用者はファクトリに生成を依頼するだけで、オブジェクトの生成手順や種類を意識する必要はなく、望むオブジェクトを使用できる状態で手に入れることができます。
 もし生成するクラスの種類や作成手順が変更されても、ファクトリの中を手直しするだけですみます。

サルでもわかる 逆引きデザインパターン より

Alan Shalloway / James R. Trottさん

オブジェクトの生成と管理を検討する段階で従うべき一般的な規則があります。それは、「オブジェクトは、他のオブジェクトの生成および/あるいは管理するか、他のオブジェクトを使用するかのどちらかのみを行い、双方を行ってはならない」というものです。

『デザインパターンとともに学ぶオブジェクト指向のこころ』 より

GoFのFactory Methodパターンとの違い

GoFのデザインパターンでは、Factoryパターンではなく、Factory Methodパターンが紹介されています。Factory Methodパターンでは、Factoryクラス(今回の場合はURLクラス)が抽象クラスとなり、その具象クラスであるXXXFactoryクラス(今回の場合はHttpURL/JarURLクラス2)が、対応するインスタンス(今回の場合はHttpURLConnection/JarURLConnection)を作成することになります。

もし今後、XXXURLConnectionのようなURLConnectionの派生クラスが増える可能性が高いなら、URLを抽象クラスにして、XXXURLのようなFactoryクラスを作れるようにした方がいいかと思います。しかし、設計者はその可能性は低いと考えたのでしょう。現状の設計の方がシンプルなので、使用者にとっては使いやすいのです。

最後に

わざわざ美術館に行かなくても、たった数行のコードを眺めるだけで知的な愉しみを味わうことができるのは、プログラマーの醍醐味でしょう。

Factoryパターンの芸術性に共感してくださったエンジニアの方は、ぜひ当社(クオリサイトテクノロジーズ株式会社)の採用担当までご連絡ください!

関連記事

インスタンスを作る

インターフェイスをシンプルにする

他のクラスに任せる

参考URL


  1. JarURLConnectionクラスの使い方の例は、参考URLをご覧ください。 

  2. 実際には存在しません。