シスアーキ in はてな

シスアーキ(自称)の技術ブログ

アジャイルAPI設計時代の到来!?APIデザインの極意を読みました。

プログラミング言語Java」「Effective Java」などの翻訳で有名な、柴田芳樹さんの新たな訳書である「APIデザインの極意」を読みました。

APIデザインの極意 Java/NetBeansアーキテクト探究ノート

APIデザインの極意 Java/NetBeansアーキテクト探究ノート

 

 「APIデザインの極意」は、NetBeansの生みの親で、初期のアーキテクトであるJaroslav Tulach(ヤロスラフ・ツゥラッハ)が著者で、NetBeansの開発で得た経験や教訓を纏めたノートが元になって書かれた書籍です。

従来のデザインパターンでは解決できない、後方互換性を維持しながらライブラリを発展させる設計手法について書かれています。

読んだ感想としては、GoFデザインパターン本のような体系だった内容というよりは、後方互換性を意識した設計手法が必要となる背景や理由、API設計者としての思想や態度に重点がおかれているように感じました。

本書の難易度はかなり高めだと思います。読み解くにはJavaをある程度習得している必要があります。「Effective Java」の内容を理解でき、デザインパターンの知識が身に付いた中級者が次に読む本だと思います。

なお、1章は哲学的な話が続くので、読みにくく感じるかも知れませんが、2章以降はシステムよりの話になるので技術者にとっては読みにくさはなくなるかと思います。

なぜ新たなデザイン本が必要なのか

序章でも述べられていますが、様々なデザイン本が出ている中、なぜ新たなデザイン本が必要なのでしょうか?
その答えは、本書の中にある、

今日のシステムは、巨大なビルディングブロックで構成されています。

 にあると思います。

僕は最近のシステム開発はまるで、「レゴブロックの組み立て、もしくはパズルのピース合わせのようだ」と感じています。様々なフレームワークやライブラリが提供する一部のAPIを組み合わせ、または拡張し、それらをブラックボックスとして使用します。
以前、ソートのアルゴリズム名を知らないプログラマに驚いたことがありますが、幾多のライブラリを導入し、ブラックボックスとして使用する僕とそのプログラマの違いは何だろうと疑問を感じたことがあります。そして、このような開発手法にも若干の違和感を抱いておりました。

本書ではこの状況を以下のように述べています。

21世紀の最初の10年間に構築される平均的なシステムは、その裏側にはなんの優雅さがない、あってもわずかしかない巨大なゴミの山のようなものです。そのようなシステムを構築する主な動機は、常に最小限の努力で成し遂げることです。そのために、エンジニアリングチームは、既存のソフトウェアフレームワークが必要以上であっても再利用しようとします。

 自分の知識や経験を踏まえて今後のシステム開発のあり方を考えた場合、このような開発手法は今後もますます拡がっていくと想定しています。

僕らは「何をしたいか」に注力し、大きなビルディングブロック上で自分がしたい”わずかな”拡張を実装することでそれを実現することになるでしょう。これを実現する為には、ユーザーがソートを実現したい時にそれを実現する方法を簡単に発見出来て、(具体的なアルゴリズムを知らなくても)それを呼び出すだけで最適なアルゴリズムによりソートした結果が得られるAPIが必要となります(本書では、そうした状況を選択的無知と呼称しています)。そして、APIを提供する設計者は、ユーザーの投資を無駄にせずに発展できるような設計、つまり後方互換を維持できるAPI設計が重要となります。

今までのデザイン本ではこのような手法については述べられていませんでした。
それが著者が本書を書いた動機であり、今後の開発者が本書から学ぶべき内容だと思います。

本書は400パージ程度の一般的なボリュームの技術本ですが、1ページあたりの内容が濃く、全てを端的に纏めることは不可能です(むしろ、著者が纏めた結果が本書のページ数です)。ですので、ここでは本書の概要と、中でも僕が感銘を受けたいくつかの内容について紹介したいと思います。

本書の概要

序章で述べられていますが、

が本書のテーマです。

  • 純粋に芸術的な設計よりは多少エンジニアリング的設計を好むすべてのAPIアーキテクト

が本書の想定読者層となっています。

第1部では、本書で述べる語彙が定義されています。
第2部では、具体的なAPIの設計手法
第3部では、API設計者としてうまくやっていくための助言が述べられています。

本書は精読が必要な部類の本であり、第1部から順に理解しながら読んでいくことをお勧めします。

本書の内容の抜粋と感想

1章、2章では、この本の重要なテーマである「選択的無知」について説明されています。

「無知」という言葉は一般的に侮蔑や軽蔑を含むイメージがありますが、本書ではこのような「攻撃的な意味」では使われていません。

車や携帯電話については仕組みを全く気にしないで使っています。

車を運転するために車の設計を理解したいと思わないでしょう。歯を綺麗にするために科学を理解する必要はありません。

何を深く知り、何を深く知る必要がないかを積極的に選択することを意味しています。

目標は、開発者がすべてを知らなくてもよいコーディング方法を見つけることです。すなわち、開発者が必要な知識を選択することです。私は、それを選択的無知と呼びます。

と本中で述べられているように、優れたAPIは無知であることを選択できます。

また、

無知モデルは、世界中のソフトウェアプロジェクトから集められた大きなビルディングブロッグを使用することに基づいています。

 と本中で述べられていますが、今日のシステムはより大きく、そしてより複雑になっています。昔のように、限定された単一環境上で画面入力をファイルに読み書きするだけのシステム開発はほとんどなく、複数の異なるベンダーが提供するOS、データベース、アプリケーションサーバーを利用し、様々なフレームワークやライブラリを組み合わせ、各所に分散されたシステムと協調して機能を実現するのが今日の一般的なシステムの姿です。そして、そのようなシステムでは、様々なAPIが他のAPIに依存し、ツタのように絡み合い、複数APIが異なるバージョンの同一APIを使用するというややこしい状況が普遍的に発生します。

このような状況下において、これらのAPIを組み合わせて効率よくシステムを設計・開発し、更にはエンハンスしながら健全に保つには、

  • 無知な状態を選択出来ること
  • 無知な状態で居られること

に気を使うことが、今後ますます重要になると思います。
それを実現するには、無知な状態でAPIを導入・使用でき、そしてAPI後方互換性を保ちながら健全に発展出来る設計手法を学ぶことが重要になってきます。

 3章、4章では、APIに焦点をあてた内容が説明されています。

について述べられています。

API」は、それ以上に広範囲な意味を持つ用語であり、他の多くの種類のインタフェースを含んでいます。

と本中にあるように、一般的なプログラマが想像する以上のものがAPIに含まれます。
それは一般的に想像する「メソッドとフィールドのシグネチャ」のみではなく、環境変数や標準出力やログ、振る舞いといった広範囲なものがAPIに含まれます。標準出力までもがAPIとして扱われることには驚きましたが、Unixのコマンドの使い方を考えると、すごく納得できました。*1
ある変更がユーザーを「びっくりさせる」のであれば、それはAPIとして含まれるということになります。

 APIの品質検査方法については、API設計者はもちろん、APIユーザの視点でも学んでおくことが重要だと思います。

ビルディングブロックの上で効率よくシステムを開発する為には、APIを設計する以上に、より良いAPIを探し出し、評価し、使用することが重要なスキルになります*2

API導入の善し悪しがシステム開発から保守・エンハンスまで、システムのライフサイクル全体の品質に影響します。APIを一度導入すると、そのAPIはアプリケーションの一部となり、今後の保守対象となります。導入するAPIの習得コスト、発展性を評価し、「投資の保全」が保てるよう、APIの品質検査方法を押さえておく必要があります。

余談 ①

以前、とある勉強会で「Jenkinsがすごい後方互換の維持を頑張っている。その為に、Javaバイトコードにパッチをあてるなど、黒魔法的なことをしている。」という話を聞きました。NetBeans開発でも同様の手法がとられていたことが本書でも記載されており、なんというかSunの後方互換に対する遺伝子のようなものを感じました。またJenkinsはユーザーの「投資」に真摯に向き合っているのだな〜と感動しました。 

API後方互換のレベルとしては、

  • ソース互換性
  • バイナリ互換性
  • 機能互換性

が挙げられています。
特に、機能互換性については考慮が漏れがちです。

のような理由で機能互換性を失う改修をすることがありますが、そのような改修はAPIの提供範囲によっては大きな影響をもたらします。

APIの提供範囲により、「どこまで」後方互換を保つかのレベルは変わると思いますが、後方互換のレベルを知り、それを保つ為の設計手法であったり、保ったまま発展させる方法を知識として押さえておくことは重要だと思います。

余談②

すこしうる覚えですが、以前、とあるORMapperの設計者がTwitterで「nullをパラメータに渡されたときに、"is null"を指定されたように設計変更しようかどうか」を悩んでいたのを見かけたことがあります。結局、その設計変更は他のフォロアーの「そのような変更はオプション指定で行えるようにしてほしい」という意見で見送られたみたいですが、大変興味深い内容でした。この会話内容には、本書の後方互換性の改修に対する最適解が含まれています。

 

第2部では、具体的なAPIの設計手法が述べられています。

  • 御託はいいから具体的な設計手法を知りたい
  • とりあえずコードを読みたい

といった開発者にとっては、第2部からが楽しい内容になると思います。

5章、6章では、API後方互換性を維持しながら健全に発展できる為の具体的な手法がJava言語に特化して述べられています。特に僕が感銘を受けた内容*3は、

  • メソッドを安全に追加できる型としてfinalクラスを使用する
  • 不変型を指定するためにJavaインタフェースを使用する

の2点です。

「不変型を指定するためにJavaインタフェースを使用する」については、Java8でインタフェースにデフォルトメソッドが導入される経緯となった理由を理解する上でも面白い内容だと思います(更に言えば、Java8以降についてはこのデザインパターンもデフォルトメソッドの出現で再考が必要となります)。

メソッドを安全に追加できる型としてfinalクラスを使用する」については、オブジェクト指向の代名詞と言われる継承に対する否定でもあり、とても面白い内容です。継承は「無知」をサポートするには難しい言語機能であり、ユーザの拡張方法が多岐に渡るので、後方互換の維持も難しくします。また、容易にソースをスパゲティ化することも出来ます。JavaではCompositeパターンが言語レベルでサポートされておらず、それを実現するには「もっさり」したコードを記述する必要がありますが、それでも本書を読んで、APIとして継承のサポートは出来るだけしないほうがいいように感じました。

8章ではJavaのWriterを例として、Decoratorパターンの発展問題と、それに対処する方法が述べられています。Decoratorパターンは僕が好むデザインパターンの1つであり、美しさを感じていたのですが、APIの発展性を考えた場合には大きな問題点があることに驚きました。APIは使用者向けと提供者(サービスプロバイダー)向けに分けて提供することの重要性、そして、コアな部分とサポートの部分に分けて提供することの重要性が理解出来ました。

9章では、テストについて述べられています。テストについては本書の中で心に響く文章があったので紹介しておきます。

テストは病みつきになるという私の主張にみなさんが同意しないかもしれない唯一の理由は、みなさんが試してみたことがないということです。

仕様書は時代遅れで、少なくともそのように思えます。...実際、テストは非公式な仕様書の一部となり、公式な仕様書がないことに対する埋め合わせとしての役割を果たします。

 また、テストをサポートするテストAPIを提供することの重要性や、テスト互換性キット(TCKを提供することの重要性が述べられています。

10章では他のAPIとの協調においての注意事項が述べられています。他のライブラリのAPIをほとんどそのままのシグネチャーで公開する、再エクスポート(re-export)を行った場合の発展性への影響などが述べられています。

11章では、API実行時の側面について、スレッドを意識した話が多く挙げられています。特にスレッド周りのテストではいつも苦労するのですが、本書ではログを使用したクールなテスト手法が紹介されており、とても興味深い内容でした。

12章では、宣言型プログラミングについて述べられています。特に、「オブジェクトを不変にする」の節で述べられている、

宣言型プログラミングの力は、高いレベルの概念を定義し、低いレベルで何が起きるかの詳細を正確に指定させるのではなく、高いレベルで指定させることができるのです。

関数型言語の時代が来たら、コーディングは突然、さらに美しくなり、はるかに優雅になり、常に安定していて真実は永久に変わらない幾何学のようでしょう。それまでは、私たちのAPIの一部を少なくとも不変にすることで、来るべきその時代を感じることができます。

については、選択的無知や後方互換性の観点からも、今後APIはより関数型プログラミングに向いて進化することを彷彿させました。

 

第3部では、API設計者としてうまくやっていくための助言が述べられています。
特に、13章の有害で極端な助言については、API設計者は一度目を通しておいたほうがいいでしょう。

僕の場合で言えば、「APIは、正しくなければならない」の節で述べられている、

Javaでディスクからファイルを読み込む方法が異常に「面倒」と思っているのが、私だけではないと確信しています。

の一文がAPIを正しく設計するだけでは良い設計といえないということを実感をもって理解できて面白かったです。

余談③

Java7ではFileを簡易的な扱うjava.nio.file.Filesが提供されておりますが、本来はFileの追加メソッドとして提供されたほうがより利便性が良かったと思います。
そうならなかったのは、広く使われているだろうFileクラスがfinalでなかったことが原因だったのではないかと推測しています。

個人的には、3部では17章のAPI設計フェストが一番面白かったです。
本書を読む方は17章で設計フェストに挑戦し、本書の内容が身に付いているかを実感して欲しいと思います!*4

まとめ

先に述べたとおり、本書は内容が濃く、全てを端的に纏めることは不可能なほど示唆に富んだ内容が豊富に述べられています。
ライブラリを開発して世に広めようとしている意識の高い技術者、ライブラリを導入して"上手く"システムを開発しようとする技術者のいずれにとっても本書の内容は有効なものになるでしょう。
世の中のライブラリが「選択的無知」を活用し、"下手な"APIがより多くの死者を生み出さない為にも、是非本書をより多くの優秀な技術者の方が読み解き、そしてその設計手法を広めていって欲しいと思います。

 

APIデザインの極意 Java/NetBeansアーキテクト探究ノート

APIデザインの極意 Java/NetBeansアーキテクト探究ノート

 

 

プログラミング言語 Java 第4版

プログラミング言語 Java 第4版

 

 

EFFECTIVE JAVA 第2版 (The Java Series)

EFFECTIVE JAVA 第2版 (The Java Series)

 

 

*1:Unixではコマンドをパイプやリダイレクトで組み合わせて「やりたいこと」を実現しますが、標準出力の内容がコマンドのバージョンによって変わると、おそらく混乱を招き使い物にならないでしょう。

*2:そのような情報を求めて雑誌を購読したり、勉強会に参加したりもします。

*3:ほとんど感銘を受けているのですが、全て述べるとキリがないので。

*4:ちなみに僕はボコボコにされましたがww