torutkのブログ

ソフトウェア・エンジニアのブログ

「APIデザインの極意」を読む会第3回を終えて

12月27日(土)に、Java読書会BOF主催の「APIデザインの極意」を読む会(第3回)を実施しました。1998年12月にJava読書会BOFの源泉であるJava互助会主催で始まった読書会も今回で通算189回、書籍としては28冊目、17年目に突入しました。参加人数も毎回コンスタントに10人を越えています。来年も毎月一回のペースで開催していく予定です。

現時点での開催予定

Java読書会BOFのWebサイト
http://www.javareading.com/bof/

APIデザインの極意」を読む会第2回(2014-11-29開催)のふりかえり

先月開催した内容ですが、簡単に振り返ります。

p.40「API設計者の最も重要な責任は、そのライブラリを使用する人々の投資が無駄にならないようにすること」

  • これは当たり前ですが忘れがちなので、肝に銘じておきたい文言です。

p.44「古いバージョンのAPIに対して作成されコンパイルされているクライアントがAPIの新たなバージョンで機能することは、後方互換性(backward compatibility)と呼ばれます。」

  • ライブラリ利用者にとってライブラリ側の都合でバージョンアップされたときに、API呼び出しを修正しないといけないとしたら、そんなライブラリは使いたくなくなります。後方互換性は重要です。

p45「ライブラリの以前のバージョンでコンパイルされたプログラムをリコンパイルしないで新たなバージョンのAPIとリンクできれば、バイナリ互換性(binary compatibility)を達成することができます。」

  • ライブラリを設計する側ではこれを目指したいところです。

Javaでは、パッケージ内のクラスをpublicとしなければクライアントから利用できません。そこで、API(公開する)クラスをpublicとし、それ以外のAPI実装クラスを非publicとすることでAPIを明確にできます。
しかし、大規模なライブラリでは、機能を提供するパッケージが複数に分かれ、APIでなくてもAPI実装で使うクラスを別パッケージで使用する必要があります。内輪のパッケージであればアクセス可、外側のパッケージからはアクセス不可とすることができれば目的を達成できます。しかし、Javaではそのような機能は用意されていません。一時期、この状況を解決する将来のJavaの機能としてスーパーパッケージが提案されていましたが、今はどこかに消えてしまっています。
Java SE 9で提供を計画しているProject Jigsawには、この問題へのアプローチはありません。

APIデザインの極意」では、この目的を達成する技法として、フレンド・アクセッサ・パターン(Friend Accessor Pattern)を紹介しています。

API(公開クラス・メソッド)を提供するパッケージと、APIを実装するパッケージがあったとします。APIパッケージは誰でもアクセスできますが、API実装パッケージはAPIパッケージからだけアクセスができるようにします。

APIパッケージにある非公開メソッドを、API実装パッケージから触るためのアクセスメソッドを定義したクラスAccessorをAPI実装パッケージに作成します。

以下は、APIパッケージの公開クラスItemの非公開メソッドであるコンストラクタとaddChangeListenerをAPI実装クラスからアクセスするためのアクセスメソッドを定義しています。

+-----+                                   +------+
|api  +----------------------+            |impl  +---------------+
| +-----------+              |            | +-----------+        |
| | Item      |---------------------------->| Accessor  |        |
| |           |              |            | |           |        |
| +-------+--+              |            | +-----------+        |
|     ↑  ↓                 |            |    △                |
| +---+--------+            |            |    |                |
| |AccessorImpl |------------------------------+                |
| |             |            |            |                      |
| +-------------+            |            |                      |
+----------------------------+            +----------------------+
  • APIパッケージの公開クラスItemには、API実装パッケージからのみアクセス可能な非公開メソッドがある(Java言語仕様上はパッケージプライベート)。
  • API実装パッケージに、Itemクラスのパッケージプライベートメソッドをアクセスするための抽象メソッドを定義したAccessorクラスを定義
  • APIパッケージの非公開クラスAccessorImplでItemクラスのパッケージプライベートメソッドにアクセス

APIデザインの極意」を読む会第3回(2014-12-27開催)のふりかえり

JDKAPIでよくみかける設計(よくない)

+--------------+
|<<interface>> |
| Document     |   APIとなるメソッドが定義
+--------------+
       △
       |
+------+--------+
|AbstractDocument|  APIとなるメソッドに加え、便利なメソッドが追加定義
+----------------+
  • API利用者は、APIであるDocumentを使わず、AbstractDocumentへキャストして便利なメソッド(非API)を使ってしまう

インタフェースと実装(抽象)クラスという形ではなく、公開APIを提供するクラスとAPI作成者に特権アクセスを与えるクラスの2つで構成するのがよい。

著者は、古典的なオブジェクト指向の考え(動物クラスを犬、猫クラスが継承するといったような)を信じておらず、「込み入ったswitch文以上の何物でもありません」、「ここでのサブクラス化は、単に実装の詳細です」と言っています。

「継承階層が2階層を越えているのであれば、それ以上階層を増やすのは止めて、これはAPIとして行っているのか、コードの再利用のために行っているのかと自問する」

ということで、著者は、API設計においては継承を使わないことを指向しています。

NetBeansのオープンAPIでは、パッケージ構成でAPIと実装とを分離しており、org.openideとそのパッケージに公開APIを、com.netbeans.coreに実装を入れることで明確にしています。
この方法はとても賛同できます。前回のフレンド・アクセッサ・パターンと組み合わせることでパッケージレベルで公開・非公開を実現できます。
ただ、ちょっと面倒なので、スーパーパッケージがJavaで仕様化されると嬉しかったのですが・・・。

モジュール方式の設計で、Java SE 6から、java.util.ServiceLoaderが追加されたと言及がありますが、知りませんでした。

ぐぐってみると、ITProの「Java SE 6完全攻略」第11回 コンポーネントのロードを行うServiceLoaderという記事で紹介されています。さすがは櫻庭さんですね。
http://itpro.nikkeibp.co.jp/article/COLUMN/20061215/257003/

APIのデザインについて考える人は

APIを作る人、モジュール方式の設計をする人はこの本にある内容は知っておくとよいと思います。

読書会での進捗

この本は、1日の進みが他の書籍にくらべてかなり遅いです。統計的には通常1日にほぼ60ページ進むのですが、この本は40ページちょっとです。1ページあたりの文字数が通常より多いようです。また、朗読していて次の行に視線を移すときに、時折場所を見失います。行間がやや狭いためと思います。密度が濃くなっています。

誤植について

Java読書会を実施すると、精読するのでいくつか誤植を見つけることがあります。
APIデザインの極意」は、出版社のページに正誤表がありますが、まだ記載されていないものをいくつか発見しました。この本は出版社のサイトに正誤表があるので、問い合わせフォームを使って報告しました。