38
39

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-04-24

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

今回の芸術

import org.apache.http.client.fluent.Request;
import java.io.IOException;
...

String html = null;
try {
    html = Request.Get("http://www.qualysite.co.jp/")
        .connectTimeout(1000)
        .socketTimeout(1000)
        .execute()
        .returnContent()
        .asString();
} catch (IOException e) {
  ...
}

引数で指定したURLのソース(HTML)を取得するシーンですが、あらためて鑑賞してみると、直感的に伝わってくるインターフェイスの美しさにうっとりしてしまいます。

鑑賞のポイント

流れるようなインターフェイスに目が行きがちですが、設計者に言わせると「あんなの飾りです。偉い人にはそれがわからんのですよ1」という答えが返ってきそうです。ここでの鑑賞ポイントは、何かとステップが多いHTTPの接続ルーティーンを、わかりやすく、シンプルにまとめた機能美です。以下で一緒に味わっていきましょう。

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

冒頭のコードは、Javaで最も有名だと思われるHTTPクライアントApache HttpComponents HttpClientの使用例です。このライブラリには、2013年にリリースされたバージョン4.3で、冒頭のようなインターフェイスが追加されました。

それ以前は、同じ結果を得るのに以下のような、ステップが多いコードを記述する必要がありました。

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
...

String html = null;

CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet("http://www.qualysite.co.jp/");

// Requestの設定
RequestConfig requestConfig = RequestConfig.custom()
    .setSocketTimeout(1000)
    .setConnectTimeout(1000)
    .build();
httpGet.setConfig(requestConfig);

CloseableHttpResponse response = null;
try {
  response = httpClient.execute(httpGet);

  // Content取得(EntityオブジェクトがContentを保持している)
  HttpEntity entity = response.getEntity();

  // Contentからソース取得
  html = EntityUtils.toString(entity, StandardCharsets.UTF_8);

  // Entity消費
  EntityUtils.consume(entity);
} catch(IOException e) {
  ...
} finally {
  try {
    if (response != null) {
      // Responseクローズ
      response.close();
    }
  } catch (IOException e) {
    ...
  }
}

このようなコードでは、コーディングに手間がかかることは表面的な問題です。本当の問題は、コードを記述するときにミスが起きやすいことです。HTTP接続を開始してから終了するまで、いくつものステップを順番に踏まないといけないことと、必ず実行しなければいけないステップがどれなのかわかりにくいことは、ミスの原因になります。

具体的には、

// Entity消費
EntityUtils.consume(entity); // 内部ではInputStreamを閉じている

や、

// Responseクローズ
response.close(); // 内部ではHTTPコネクションを閉じている

は、ビルド時にも実行時にもエラーにならないので、内部の作りを理解していないと簡単に書き忘れてしまうでしょう。「Entityは消費(consume)しないといけない」とか、「Responseは閉じないといけない」というのは一般的ではありません。この書き忘れは、リソースリークを引き起こします。

また、CloseableHttpClientはCloseableインターフェイスを実装しているため、httpClient.close();という記述もできるのですが、このようにHttpClientをcloseするメソッドは呼ばなくても大丈夫なのでしょうか? 公式のサンプルソースでは呼ばれていないので不要かと思いますが、不安になってしまいます。

低レベルでたくさんの機能を提供していて、数々のライブラリで使われているHttpClientですが、必然性がないケースでこのコードをそのまま使うのは美しいとは言えませんよね。

Facadeパターンを使った場合

冒頭のコードはFacadeパターンが使われています。コメント付きでもう一度見てみましょう。

import org.apache.http.client.fluent.Request;
import java.io.IOException;
...

String html = null;
try {
  html = Request.Get("http://www.qualysite.co.jp/")
    .connectTimeout(1000) // Requestの設定
    .socketTimeout(1000)  // Requestの設定
    .execute()
    .returnContent()      // Content取得 -> Reponseクローズ -> Entity消費
    .asString();          // Contentからソース取得
} catch (IOException e) {
  ...
}

注目すべきは、Response#returnContentメソッドです。このメソッドひとつで、Content取得 -> Reponseクローズ -> Entity消費という、複数のオブジェクトを使う処理で必ず実行しなければいけないステップを、順序通りまとめて処理できます。

このメソッドでは3つの処理を行っていますが、メソッドの名前がreturnContentなのもポイントです。クラスの設計者は、ReponseクローズとEntity消費の処理をクラスの利用者に意識させたくなかったのでしょう。メソッドに含まれていないからです。ただ、さらに想像すると、getContentではなくreturnContentとしているところが、「単にContentオブジェクトの参照を返すだけではないんだよ」という隠れたメッセージも見えてくるような気がします。

Facade(ファサード)は、「建物の正面」という意味です。Facadeパターンの目的は、利用者に建物の正面しか見せないようにすること、つまり複数のクラスのごちゃごちゃしたインターフェイスをまとめて、最小限だけ見せることです。どうりでインターフェイスがエレガントだと思っていましたが、それは、設計者によって計られた美しさだったのです。

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

多くの専門家からも、Facadeパターンを評価するコメントが寄せられています。(太字での強調は私がやりました。)

結城浩さん

あるプログラマが「ああ、このクラスを呼ぶ前にはこっちを呼ぶんだよ。こっちのメソッドの呼び出し前には、このクラスに登録しておく必要があるんだぜ」といった話を「得意げ」に語るときには、Facade役を導入する必要があることを示唆しています。
『Java言語で学ぶデザインパターン入門』 より

lang_and_engineさん

複数のクラスを呼び出す際,呼び出す順番を思い出さなくてすむように,定石として1メソッド内に集約しておく。そのメソッドは,いわば複数のクラスの利用手順書になる。
GoFの23のデザインパターンを,Javaで活用するための一覧表 より

最後に

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

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

関連記事

インスタンスを作る

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

他のクラスに任せる

参考URL

  1. 機動戦士ガンダムに出てくる名言のひとつ。ジオングに脚がないことをシャアに尋ねられた整備兵は「あんなの飾りです。偉い人にはそれが分からんのですよ」と返した。(ジオング - Wikipedia)

38
39
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
38
39

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?