モジュール化と選択的無知の実現、そして後方互換性の維持へ

2014年10月22日(水)
Jaroslav Tulach(ヤロスラフ・ツゥラッハ)柴田 芳樹(しばた よしき)

分散開発への問題に対する技術的答えは、アプリケーションのモジュール化です。すべての構成単位が他の構成単位と直接干渉するように強く結び付いてしまっている一体化された塊と比較すると、モジュール方式のアプリケーションは、分離された小さなコードの塊から構成されます。それらの塊はきちんと隔離され、一意に特定され、他のコードが使用する明確なインタフェースを公開し、正しく機能するために必要な環境(他のコンポーネントや構成単位が正しく機能するために必要な環境)を注意深く記述しています。離れたチームが自分たちのサイクルとスケジュールで担当部分を開発できると考えられていますし、Linuxディストリビューションベンダによっても示されています。そして、1つの中心的な機関(ディストリビュータ)が成果物を組み立てることができます。そして、この方式は、スケジュールとチームが離れていることに起因するリスクを最小化することに役立ちます。また、無知モードでの運営も可能にします。つまり、構成単位を作り出す開発者が依存性を正しく記述すれば、組み立てる人達はコンポーネントの内部についての知識をほとんど必要としませんし、それでも、最終アプリケーションをうまく組み立てられます。

しかし、新たな問題が発生します。コンポーネントは、発展するのです。コンポーネントは、静止しているのではなく、変化していきます。バグ修正、機能向上、新たな機能の追加などにより、APIは絶え間なく変化する可能性があります。そして、結果的に、APIを一意に特定する名前だけを使用するのでは十分ではありません。独立したコンポーネント群が一緒に機能することを保証するためには、コンポーネントが実際に提供しているAPIがどれであるかを特定する必要があります。

たとえば、String.contains(String)を参照するクラスがJavaで書かれていたとしたら、Stringクラスにそのメソッドを提供しているJavaのバージョンは5なので、そのクラスはJava 5で動作できます。しかし、Java 6でも同じメソッドを利用できますので、Java 6でも動作できます。Javaチームの互換性方針により、Java 7の最新ビルドでも利用できます。一方で、このようなクラスは、そのメソッドを提供していない古いバージョンのJavaでは動作できません。したがって、古いバージョンのJavaでは、正しくリンクできません。

実際、クラスやアプリケーションの正確な要件を知る必要がある場合には、呼び出しているすべてのメソッド、および、参照しているすべてのクラスとフィールドを列挙する必要があります。もちろん、依存性の記述は、冗長で、読みにくく、時には、実際のソースコード自身より大きかったりします。クラスが、「私には、コンストラクタとlengthメソッドとindexOfメソッドを持つjava.lang.Stringを提供しているJavaのバージョンが必要です。そして、私のクラスはjava.io.Serializableにも依存しており、それを実装しています」などと言うことを想像してみてください。実際、このようなコンポーネントの実際のバージョンの詳細な仕様は、私が強調したい無知の方式には真っ向から反しています。コンピュータがこのような制約を自動的に検証したとしても、人間にとっては、難しすぎて使用できません。人間は単純な方法を好みます。たとえば、自然数を使用して、コンポーネントの各リビジョンに番号を付けます。そうすれば、「Java 5であれば私のライブラリは問題ありません」と単純に言うことができます。

しかし、単純に番号を使用することは、物事を複雑にする可能性もあります。図2-1に示されるように、(Java 5へ依存している)コンポーネントAと(Java 6へ依存している)コンポーネントBからアプリケーションを組み立てることを想像してみてください。この場合、AとBの両方を実行できる1つのバージョンのJavaが存在する可能性があるので、問題はどのバージョンを使用するかです。この問題を解くために、ほとんどのAPI開発モデルは、互換性という概念を用いてうまくいくようになっています。普通の場合、何らかのAPIがバージョンNで導入されたら、その後のバージョンN+1、N+2、N+3などでもそのAPIは存在することを意味しています。これは、図2-1の例を解決してくれ、組み立てられたアプリケーションはJava 6で実行されるべきだということです。コンポーネントBは明示的にJava 6を必要としていますし、コンポーネントAはJava 5を必要としているだけですが、Javaの2つのバージョン間の互換性のおかげでJava 6でも動作します。実際、そのことで、最終アプリケーションを組み立てる人達の労力をかなり減らしてくれます。彼らは、組み立てる際に、かなり無知になることができます。すべては、1つの「小さな」前提条件を必要とするだけです。それは、後方互換性を維持するということです。後方互換性を維持することは、全く小さな仕事ではありません。かなり複雑であり、何も知らずに行えることではありません。したがって、それが、この本全体を通して私達の関心事そのものなのです。それは、私達が注力する必要がある「選択的無知」の一部です。分散したチームにより作成されたコンポーネントから大きなアプリケーションを組み立てる際に私達の無知を最大化するためには、各チームが個々のコンポーネントとそのAPIを注意深く開発する必要があり、なおその上で、互換性がある方法で行う必要があります。

図1: 1度に2つの異なるバージョンのコンポーネントを必要とするアプリケーション

この記事のもとになった書籍
APIデザインの極意 Java/NetBeansアーキテクト探究ノート

Jaroslav Tulach 著/柴田 芳樹 訳
価格:3,200円+税
発売日:2014年05月23日発売
ISBN:978-4-8443-3591-7
発行:インプレスジャパン

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

なぜ、設計の良くないAPIを持つソフトウェアが量産されるのでしょう。エンジニアが良いAPI・悪いAPIについて分かっていない、あるいは、適切なレビューを受けていないからかもしれません。本書では、NetBeansアーキテクトの著者が遭遇してきた様々な誤りを解説し、APIの発展を考慮した設計について詳しく説明。あまり語られることがなかったAPI設計について、貴重な10年間の経験をベースに幅広くノウハウを披露。API設計の技術や知見の水平線を押し広げることができる稀有な一冊です。

Amazon詳細ページへImpress詳細ページへ

著者
Jaroslav Tulach(ヤロスラフ・ツゥラッハ)
NetBeansの生みの親で、初期のアーキテクト。NetBeansは当初、Java統合開発環境として開発され、現在はJavaScript・Ruby・PHP・C/C++などにも対応。今も、オープンソースプロジェクトで開発が続けられている。著者は、NetBeansを支える技術の生みの親として、このオープンソースプロジェクトの成功に貢献。現在も、プログラマーの設計スキルを向上させる新たな方法を探求しつつ、このプロジェクトに参加している。
著者
柴田 芳樹(しばた よしき)
1959年生まれ。九州工業大学情報工学科で情報工学を学び、1984年同大学大学院で情報工学修士課程を修了。以来、様々なソフトウェア開発に従事。ゼロックス社のパロアルト研究所を含め、5年間米国に駐在してソフトウェア開発に携わる。現在はソフトウェア開発、教育、コンサルテーションなどを業務としている。 本書の翻訳を担当。

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています