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

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

## 今回の芸術

#### ソースファイル

```java
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
...
    ...
    // メモリ上に新しいXMLドキュメントを生成
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder = factory.newDocumentBuilder();
    Document document = builder.newDocument();

    // XMLの要素を生成
    Element element = document.createElement("element");
    Text text = document.createTextNode("text");
    Attr attribute = document.createAttribute("attribute");
    ...
```

JavaでXMLのドキュメントと要素を生成するシーンですが、あらためて鑑賞してみると、重厚でミステリアスなインターフェイスに見入ってしまいます。


## 鑑賞のポイント

XMLの`Document`インスタンスを生成するために直接newするのではなく、わざわざ`DocumentBuilderFactory`クラスと`DocumentBuilder`クラスを介しています。機動戦士ガンダムでガンダムを苦しめた、ドムによるジェットストリームアタック（ドムのパイロット3人組による攻撃）[^1]を連想させる3連続の`newXXX`のインターフェイスですが、正直なところ、冗長さが目につき、あまり美しいようには見えません。しかし、裏に隠れているソースコードを読んでみれば、何か感じることがあるかもしれません。設計者の想いを一緒に探っていきましょう。


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

それではまず、最も直感的なコードで、XMLのドキュメントと要素を生成してみましょう。なお、このセクションのソースコードは、説明のため、架空のクラスを含みます。

```java:Factoryパターン
// Documentは要素のFactoryクラスに相当
Document document = new Document();

// XMLの要素を生成
XMLElement element = document.createXMLElement("element");
XMLText text = document.createXMLTextNode("text");
XMLAttr attribute = document.createXMLAttribute("attribute");
...
```

この用途だけだと特に問題はありません。では、今度は、XMLの要素の他に、SOAP[^2]の要素も生成できるようにしてみます。

```java:Factoryパターンの続き
// SOAPの要素を作成
SOAPElement element = document.createSOAPElement("element");
SOAPText text = document.createSOAPTextNode("text");
SOAPAttr attribute = document.createSOAPAttribute("attribute");
...
```

すると、`createSOAP...`のように、`createXXX...`バリエーションが増えてしまいます。XML/SOAPなどのドキュメントの種類をひとつ増やすだけで、既存のクラス（`Document`）にたくさんの`createXXX...`メソッドを追加しないといけないのは美しくありませんね。


## Abstarct Factoryパターンを使った場合

ここでAbstract Factoryパターンを適用してみましょう。まずはXMLドキュメントの生成からです。

```java:AbstractFactoryパターン（Factory）
// XMLドキュメントを生成する場合（DocumentはFactoryクラスに相当）
Document document = new XMLDocument(); // Documentのサブクラス
```

SOAPドキュメントの生成は以下のように行います。

```java:AbstractFactoryパターン（Factory）
// SOAPドキュメントを生成する場合（DocumentはFactoryクラスに相当）
Document document = new SOAPDocument(); // Documentのサブクラス
```

要素の生成は、どちらの場合でも以下のように行います。

```java:AbstractFactoryパターン（Factoryが生成するモノ）
// 要素を生成
Element element = document.createElement("element");
Text text = document.createTextNode("text");
Attr attribute = document.createAttribute("attribute");
```

抽象化されたFactoryに相当する`Document`クラスを継承して、具体的なFactoryに相当する`XMLDocument`、`SOAPDocument`というクラスを作ります。（このクラスは架空のものです。）すると、XML/SOAPなどのドキュメントの種類を増やしても、`createXXX...`メソッドを追加する必要がなくなりました。また、同時に、Factoryが生成するモノ（`Element`、`Text`、`Attr`など）も抽象化したため、`XMLElement`/`SOAPElement`などの要素の種類にとらわれず同じように扱うことができます。シンプルで美しいですね。

Abstract FactoryパターンのGoFでの定義は「関連、または依存しあうオブジェクトのファミリを、その具象クラスを指定することなしに生成するインターフェイスを提供する[^3]」です。ここでの「関連、または依存しあうオブジェクトのファミリ」は`Element`、`Text`、`Attr`に相当します。また、「その具象クラスを指定することなしに生成するインターフェイス」は`Document`に相当します。


## Abstarct Factoryパターンはむずかしい？

Abstarct Factoryパターンは上記の通りとてもシンプルなのですが、「むずかしい」と思われがちです。私もエンジニア1,2年目の頃はピンときていませんでした。（大丈夫、あの[結城浩さん](https://www.amazon.co.jp/%E7%B5%90%E5%9F%8E-%E6%B5%A9/e/B003UW60OA/ref=pd_sim_14_bl_1?_encoding=UTF8&refRID=YDTDY91X8NYH1QKXNTMF)でも一番難しいと感じたパターンとのことです。）むずかしく感じる理由は、登場するクラスの数が多いこともありますが、それ以上に、**何がうれしいのかわかりにくい**ことにあると思います。

正直、上記の例や、デザインパターン入門の[WEB](https://en.wikipedia.org/wiki/Abstract_factory_pattern#Java_example)や書籍の例程度では、Abstract Factoryパターンを使っても、そんなにうれしいことはありません。これらでは、パターンの本質をわかりやすくするためにプログラムがシンプルになっているのですが、実際にAbstract Factoryパターンの恩恵を受けられるのは、プログラムがある程度複雑なときなのです。


## Abstract Factoryパターンの恩恵を受けられるシーン

結論から言うと、Abstract Factoryパターンの恩恵を最も受けられるシーンは、抽象化されたFactoryクラスの生成を隠蔽したいときだと思います。

図にすると以下のようになります。「最低限の使い方」と「本領発揮の使い方」です。

```text:AbstractFactory（最低限の使い方）
|呼び出し元|
↓　　　　↓
↓使う　　↓Factoryを選んで生成する
↓　　　　↓
↓　　|抽象化されたFactoryを　|
↓　　|継承する具体的なFactory|
↓　　　　↓
↓　　　　↓生成する
↓　　　　↓
|Factoryで作るモノ|
```

```text:AbstractFactory（本領発揮の使い方）
|呼び出し元|
↓　　　　↓
↓使う　　↓使う
↓　　　　↓
↓　　|抽象化されたFactory|
↓　　　　↓
↓　　　　↓Factoryを選んで生成する
↓　　　　↓
↓　　|具体的なFactory|
↓　　　　↓
↓　　　　↓生成する
↓　　　　↓
|Factoryで作るモノ|
```

「最低限の使い方」と「本領発揮の使い方」では、「Factoryを選んで生成する」個所が異なります。最低限の方では、呼び出し元が抽象化されたFactoryの実装クラスを決めます。一方、本領発揮の方では、抽象化されたFactoryクラスが動的クラスローディング[^4]や設定ファイルを使ってFactoryの実装クラスを決めます。そのため、呼び出し元ではソースコードを変えずに、具体的なFactoryの実装クラスを入れ替えることができます。

ここで、ページ冒頭のコードをもう一度見てみましょう。

```java
// 要素のFactoryクラスに相当するDocumentを生成
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.newDocument();

// 要素を生成
Element element = document.createElement("element");
Text text = document.createTextNode("text");
Attr attribute = document.createAttribute("attribute");
```

`DocumentBuilderFactory`は、Abstract Factoryパターンの「抽象化されたFactory」に相当します。`newInstance`メソッドで、この`DocumentBuilderFactory`のサブクラスである「具体的なFactory」を生成します。具体的なFactoryの実装クラスは、以下の中から好きなベンダーのものを選ぶことができます。

##### 具体的なFactoryの実装クラスのリスト

- com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
- com.google.gdata.util.common.xml.parsing.SecureGenericXMLFactory
- com.meterware.httpunit.dom.DocumentBuilderFactoryFilter
- com.sun.msv.verifier.jaxp.DocumentBuilderFactoryImpl
- net.sf.saxon.dom.DocumentBuilderFactoryImpl
- org.allcolor.xml.parser.CDocumentBuilderFactory
- ...

これらの実装クラスは、`DocumentBuilderFactory`の`newInstance`メソッド内で、次のような順番で自動的に見つけ出されます。

+ Javaの[システムプロパティ（Java VM起動時のコマンドライン引数）](http://qiita.com/yoshi389111/items/7af43d8d21efdc32c06c)で指定されたクラス
+ JREディレクトリ内の`lib/jaxp.properties`ファイルに書かれたクラス
+ jarファイル内の`META-INF/services/javax.xml.parsers.DocumentBuilderFactory`ファイルに書かれたクラス（JavaのServiceLoaderの仕組みが使われる）
+ デフォルトの実装クラス（＝`com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl`）

そのため、**実装クラスを変えても、呼び出し元のコードを一切変更しなくてよい**のです。この仕組みには、芸術性を感じずにはいられません。

なお、冒頭のコードには、Abstract Factoryパターンが2つ隠れています[^5]。それぞれ以下のような構成になっています。（Factoryという言葉に対してFactoryで作るモノをProductと表現しています。）

##### 1つめのAbstract Factory

<dl>
  <dt>抽象化されたFactory</dt><dd>DocumentBuilderFactoryクラス</dd>
  <dt>具体的なFactory</dt><dd>上記リスト（「具体的なFactoryの実装クラスのリスト」）のクラス</dd>
  <dt>抽象化されたProduct</dt><dd>DocumentBuilderクラス</dd>
  <dt>具体的なProduct</dt><dd>上記リストのクラスが生成するDocumentBuilderの実装クラス</dd>
</dl>

##### 2つめのAbstract Factory

<dl>
  <dt>抽象化されたFactory</dt><dd>Document</dd>
  <dt>具体的なFactory</dt><dd>上記リスト（「具体的なFactoryの実装クラスのリスト」）のクラスが生成するDocumentクラス</dd>
  <dt>抽象化されたProduct</dt><dd>Element, Text, Attrクラス</dd>
  <dt>具体的なProduct</dt><dd>上記リストのクラスが生成するElement, Text, Attrの実装クラス</dd>
</dl>


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

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

アラン・シャロウェイさん、ジェームズ・R・トロットさん

> 例えば、マルチプラットフォーム上で動作するシステムのユーザインターフェイスを開発している場合、使用しているOS別に用意されたインターフェイスオブジェクト群を使用することになります。このように、状況に応じてオブジェクト群を使い分けるために、Abstract Factoryパターンを使用することができるわけです。

> [『オブジェクト指向のこころ』](https://www.amazon.co.jp/%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91%E3%81%AE%E3%81%93%E3%81%93%E3%82%8D-SOFTWARE-PATTERNS-%E3%82%A2%E3%83%A9%E3%83%B3-%E3%82%B7%E3%83%A3%E3%83%AD%E3%82%A6%E3%82%A7%E3%82%A4/dp/4621066048/) より

株式会社ジグスさん

>  よく似たパターンに「Factory Method」パターンがありますが、「Factory Method」パターンは、『オブジェクト生成』の抽象化にポイントを置いたパターンであるのに対し、「Abstract Factory」パターンは、『関連するオブジェクト郡をまとめて生成するための手順』の抽象化にあります。

> [IT専科 - デザインパターン入門 > Abstract Factory パターン](http://www.itsenka.com/contents/development/designpattern/abstract_factory.html) より


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

Abstract Factoryパターンの芸術性に共感してくださったエンジニアの方は、ぜひ当社（[クオリサイトテクノロジーズ株式会社](http://www.qualysite.co.jp/recruiting/career/)）の採用担当までご連絡ください！


## 関連記事
#### インスタンスを作る
- [よく使うJavaライブラリで味わうデザインパターン - Factoryパターン](http://qiita.com/takuya-s/items/7ffebaee2a9db4310460)
- [よく使うJavaライブラリで味わうデザインパターン - Builderパターン](http://qiita.com/takuya-s/items/3c3415962a7568c637ed)
- よく使うJavaライブラリで味わうデザインパターン - Abstract Factoryパターン

#### インターフェイスをシンプルにする
- [よく使うJavaライブラリで味わうデザインパターン - Facadeパターン](http://qiita.com/takuya-s/items/eb663424a21094303b95)
- [よく使うJavaライブラリで味わうデザインパターン - Adapterパターン](http://qiita.com/takuya-s/items/45176b2bfc9fe07a9ce7)

#### 他のクラスに任せる
- [よく使うJavaライブラリで味わうデザインパターン - Template Methodパターン](http://qiita.com/takuya-s/items/8898e9bae34acada6480)
- [よく使うJavaライブラリで味わうデザインパターン - Strategyパターン](http://qiita.com/takuya-s/items/473699b5cb2d5329eed3)


## 参考URL
+ [Factory Method と Abstract Factory の違いを順に理解する](http://futurismo.biz/archives/2805)
+ [Abstract factory pattern - Wikipedia](https://en.wikipedia.org/wiki/Abstract_factory_pattern#Java_example)
+ [AbstractFactory パターン - TECHSCORE](http://www.techscore.com/tech/DesignPattern/AbstractFactory.html)
+ [DOM: XMLデータを作成する(1)](http://www6.airnet.ne.jp/manyo/xml/dom_java/step21.html)
+ [DocumentBuilderFactory (Java Platform SE 8)](
https://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html#DocumentBuilderFactory)
+ [DocumentBuilderFactoryの実装一覧 - Stack Overflow](
https://stackoverflow.com/questions/19593661/how-documentbuilderfactorynewinstance-is-an-example-of-abstract-factory-patte#answer-19593890)
+ [Java でプラグイン機構を簡単に実現する - mallowlabsの備忘録](http://d.hatena.ne.jp/mallowlabs/20140227/java_plugin_with_serviceloader)
+ [JAXPによるDOM解析](http://msugai.fc2web.com/java/XML/JAXPDOM.html)


[^1]: [黒い三連星 - Wikipedia](https://ja.wikipedia.org/wiki/黒い三連星)
[^2]: XMLで通信するためのプロトコル。HTTP上で使われる。
[^3]: [『オブジェクト指向における再利用のためのデザインパターン』](https://www.amazon.co.jp/%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E5%86%8D%E5%88%A9%E7%94%A8%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3-%E3%82%AC%E3%83%B3%E3%83%9E-%E3%82%A8%E3%83%AA%E3%83%83%E3%82%AF/dp/4797311126/ref=sr_1_1?ie=UTF8&qid=1495503419&sr=8-1&keywords=%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E5%86%8D%E5%88%A9%E7%94%A8%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3) より
[^4]: カッコつけて書いていますが、単に、Java標準の[ClassLoader](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/ClassLoader.html)を使って、ソースコードに文字列でハードコードされたパッケージ名とクラス名からインスタンスを生成する方法のことです。
[^5]: 厳密に言うと、1つめのAbstract Factoryは、抽象化されたProductが`DocumentBuilder`の1種類しかないため、GoFで定義されているAbstract Factoryパターンではありません。