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

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

## 今回の芸術

#### ソースファイル

```java:Main.java
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class Main {

    public static void main(String[] args) {
        // GSONのインスタンスを作成
        Gson gson = new GsonBuilder()
                .setFieldNamingStrategy(new MyFieldNamingStrategy())
                .create();

        // JavaのオブジェクトをJSON文字列に変換
        String json = gson.toJson(new ExampleBean());

        // 出力
        System.out.println(json);
    }
}
```

```java:MyFieldNamingStrategy.java
import com.google.gson.FieldNamingPolicy;
import com.google.gson.FieldNamingStrategy;
import java.lang.reflect.Field;

public class MyFieldNamingStrategy implements FieldNamingStrategy {

    @Override
    public String translateName(Field field) {
        // メンバ変数の"_"を取り除く
        String fieldName = FieldNamingPolicy.IDENTITY.translateName(field);
        if (fieldName.startsWith("_")) {
            fieldName = fieldName.substring(1);
        }
        return fieldName;
    }
}
```

```java:ExampleBean.java
public class ExampleBean {
    // メンバ変数の先頭に"_"が付いている
    private String _firstField = "value";
    private String _secondField = "vaule";
}
```

#### 実行結果
```
// メンバ変数の先頭の"_"が取れている
{"firstField":"value","secondField":"vaule"}
```

Gsonライブラリを使って、JavaオブジェクトをJSON文字列に変換するシーンです。あらためて鑑賞してみると、シンプルさとカスタマイズ性を両立させていそうなインターフェイスに胸が高まります。


## 鑑賞のポイント

Gsonは、Javaオブジェクト <=> JSON を相互変換する、Google製のオープンソースライブラリです。このGsonライブラリを使って、`ExampleBean`クラスをJSONに変換しています。

`ExampleBean`クラスは、C++やObjective-Cのプログラマーによって書かれたのでしょう。Javaでは珍しく、メンバ変数の先頭に"\_"（アンダースコア）が付けられています。しかし、今回生成するJSONには、"\_"は付けたくありません。

Gsonでは、JavaオブジェクトをJSONに変換する際にフィールド名（JSONのキー名）を決めるロジックとして、`FieldNamingStrategy`を`GsonBuilder`にセットする設計になっています。前回の記事で書いた「[Template Methodパターン](http://qiita.com/takuya-s/items/8898e9bae34acada6480)」では、機能をカスタマイズするために親クラスを継承する仕組みになっていたこと対して、今回はクラスのインスタンスをセットするようになっています。機能のカスタマイズにおいて、継承をできるだけ使わないことに設計の美学がありそうです。一緒に探っていきましょう。


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

冒頭のコードではStrategyパターンが使われていますが、まずはStrategyパターンを使わない場合どのような設計になるのか、Gsonライブラリの設計者になった気持ちで考えてみましょう。

やりたいことは、フィールド名に"\_"が付いているクラスをJSONに変換する際に、"\_"を付けないようにすることです。なお、変換対象のクラスはたくさんあること、また、クラスをリファクタリングしてフィールド名を変更することはできないという前提です。


#### 継承を使った設計

オブジェクト指向プログラミングにおいて最初に語られるのはクラスの継承ですので、継承を使った設計を試してみましょう。フィールド名を決めるロジックを、`Gson`クラスを継承して実装してみます[^1]。

```java:MyGson.java
public class MyGson extends Gson {
    @Override
    protected String translateFieldName(Field field) {
        // フィールド名の先頭に"_"が付いていたら除去した文字列を返す。
        // "_"が付いていなければそのまま返す。
    }
}
```

```java:Main.java
...
public class Main {

    public static void main(String[] args) {
        // GSONのインスタンスを作成
        Gson gson = new MyGson();

        // JavaのオブジェクトをJSON文字列に変換
        String json = gson.toJson(new ExampleBean());

        // 出力
        System.out.println(json);
    }
}
```

思いのほかスッキリしています…。`MyGson`クラスでは、親クラスの`translateFieldName`メソッドをオーバーライドして、フィールド名を変換するロジックを実装しています。オーバーライドしたメソッドは、親クラスである`Gson`クラスから、JSON文字列作成時に呼び出されます。この方式は前回の記事で書いた「[Template Methodパターン](http://qiita.com/takuya-s/items/8898e9bae34acada6480)」ですね。想像以上にシンプルでわかりやすくなりました。


#### 継承を使った設計の問題点

この状況下では、「Template Methodパターンの圧勝だ。Strategyパターンなんていらなかったんだ」と思われても仕方ありません。

しかし、Gsonでカスタマイズできるものはフィールド名だけではありません。[GsonのJavadoc](https://google.github.io/gson/apidocs/com/google/gson/GsonBuilder.html)を見ると、フィールド名以外にも、たくさんのカスタマイズできるポイントがあります。

1. setExclusionStrategy - JSONに含めないクラスやフィールドを定義
2. setLongSerializationPolicy - Long型を数値にするか数字にするかを定義
3. setDateFormat - Date型を文字列にする際のフォーマットを定義

などです。

これらのカスタマイズを、すべて親クラスの継承で行うと、困ったことが起きてきます。それは、機能の組み合わせが爆発してしまうということです。

たとえば、上の1の機能をオーバーライドしている`Gson1`、2の機能をオーバーライドしている`Gson2`、3の機能オーバーライドしている`Gson3`のクラスを使っているとします。

||1の機能|2の機能|3の機能|
|:-----------|:-----------|:------------|:-------------|
|Gson1クラス|○|||
|Gson2クラス||○||
|Gson3クラス|||○|

この状況で、新しく1と2の両方の機能が必要になったとき、両方のメソッドをオーバーライドした`Gson12`クラスが必要になってきます。メソッドの中身は`Gson1`と`Gson2`それぞれで記述されているものとまったく同じなので、ソースコードは重複します。

||1の機能|2の機能|3の機能|
|:-----------|:-----------|:------------|:-------------|
|Gson1クラス|○|||
|Gson2クラス||○||
|Gson3クラス|||○|
|**Gson12クラス**|**○**|**○**||

さらに、1と3の機能が必要になれば、同じように`Gson13`クラスが必要になり、1, 2, 3の機能が必要になれば、`Gson123`クラスを作る必要があります。

||1の機能|2の機能|3の機能|
|:-----------|:-----------|:------------|:-------------|
|Gson1クラス|○|||
|Gson2クラス||○||
|Gson3クラス|||○|
|Gson12クラス|○|○||
|**Gson13クラス**|**○**||**○**|
|**Gson123クラス**|**○**|**○**|**○**|
|...||||


このように、`Gson`クラスの**継承で解決しようとすると、機能の組み合わせの数だけクラスが必要になる、かつソースコードが重複する**という最悪の設計になってしまいます。

なお、継承を使ったもうひとつの方法として、JSONに変換する`ExampleBean`クラスを継承して、ここで`translateFieldName`などの各メソッドを実装することも考えられます。しかし、それでも上記と同じ問題に当ってしまいますし、そもそも、Gsonの仕様上の理念である、POJO（普通のJavaオブジェクト）のままでJSONに変換できることに反してしまいます。


## Strategyパターンを使った場合

ここでStrategyパターンを適用してみましょう。ソースコード、実行結果は[冒頭のもの](#今回の芸術)をもう一度ご覧ください。

今回のStrategyパターンの流れは以下のようになります。

1. `GsonBuilder`により、`Gson`に`MyFieldNamingStrategy`がセットされる
2. `Gson#toJson`内でフィールド名を変換する際に、`MyFieldNamingStrategy#translateName`が呼ばれる

ポイントは、フィールド名を変換する処理を`Gson`クラスで直接行うのではなく、`MyFieldNamingStrategy`に委譲していることです。このようにすることで、**メインとなるクラス（`Gson`）と、変換ロジックのクラス（`XXXStrategy`）を分離させることができます**。分離させることで、ロジックをごっそり取り替えたり、再利用したりすることが簡単にできるようになります。美しいですね！

StrategyパターンのGoFでの定義は「アルゴリズムのファミリを定義し、それぞれをカプセル化し、それらを交換可能なものにすること。Strategyパターンにより、クライアントが使用するアルゴリズムを、独立して変更できるようになる[^2]」です。ここでの「アルゴリズムのファミリ」は、フィールド名を変換するなどの機能（`XXXStrategy`クラス）に相当します。


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

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

結城浩さん

> Strategyパターンでは、そのアルゴリズムを実装した部分がごっそりと交換できるようになっています。アルゴリズム（戦略・作戦・方策）をカチッと切り替え、同じ問題を別の方法で解くのを容易にするパターン、それがStrategyパターンなのです。

> [『Java言語で学ぶデザインパターン入門』](https://www.amazon.co.jp/増補改訂版Java言語で学ぶデザインパターン入門-結城-浩/dp/4797327030/) より


lang_and_engineさん

>  ある程度複雑なアルゴリズムになると，ふつうのプログラマであれば，その部分を切り出して集約するだろう。また，他のアルゴリズムに置き変わった時に備えて，互換性も持たせておくだろう。

> [GoFの23のデザインパターンを，Javaで活用するための一覧表](http://language-and-engineering.hatenablog.jp/entry/20120330/p1) より


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

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


## 補足（GsonのTips）

上記の例では、Gsonでフィールド名を変換するために`FieldNamingStrategy`クラスを使いましたが、変換対象クラスのメンバ変数にアノテーションをつけるだけでJSONでの名前を決めることもできます。変換対象のクラスが少ないときに便利です。

```java:ExampleBean.java
public class ExampleBean {
    @SerializedName("firstField")
    private String _firstField = "value";

    @SerializedName("secondField")
    private String _secondField = "vaule";
}
```


## 参考URL
+ [How to prevent Gson serialize / deserialize the first character of a field (underscore)?](https://stackoverflow.com/questions/14965523/how-to-prevent-gson-serialize-deserialize-the-first-character-of-a-field-unde)
+ [Gsonで@SerializedNameって書くのやめたい](http://qiita.com/yysk/items/e549ba40bc2accfdff35)


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

#### インターフェイスをシンプルにする
- [よく使う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パターン


[^1]: Gsonクラスはfinal宣言されているため、実際には継承することができません。ここでは、自分がGsonの設計者だとして、どのような設計にすればよいかを検討している段階だと仮定しています。
[^2]: [『オブジェクト指向における再利用のためのデザインパターン』](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) より