プログラマが知るべき97のこと/API設計の黄金律


APIの設計は簡単ではありません。特に規模が大きいと困難になります。何百、何千というユーザが要る場合には、将来APIに変更を加えた際の影響についても考慮する必要があります。変更を加えた時、その影響でAPIを利用するクライアントコードが動かなくなるようでは困るのです。逆に、APIのユーザが開発側に与える影響についても考慮が必要です。たとえばAPIを構成するクラスの内部で、そのクラス自身のメソッドを呼び出したとします。その場合、ユーザがそのクラスを継承してサブクラスを作り、メソッドをオーバライドするという事態も考慮しなくてはなりませんし、その場合は実に面倒なことになります。APIの開発側は、もはやそのメソッドに変更を加える事ができません。ユーザが、そのメソッドに元とは違った意味を与えてしまっているからです。このように、ユーザの使い方によって、その後の開発側の内部実装に制約が加えられてしまうことがあり得るのです。

この種の問題が起きないようにする方法はいくつかあります。最も簡単なのはAPIを「ロックする」という方法です。Javaの場合は、クラスやメソッドの大半をfinal宣言してしまえばいいでしょう。C#の場合は、クラスやメソッドをsealed宣言します。言語を問わず、ともかくAPIをシングルトン、あるいはスタティックファクトリメソッドばかりにして、ユーザがその振る舞いをオーバライド出来ないようにしてしまうことは出来ます。そうすれば、ユーザの使い方によって開発側の行動が制限されるという事態は防げます。これで一応問題は解決するように思えますが、しかし本当にそう言い切れるでしょうか。

開発作業におけるユニットテストの重要性は、過去10年の間に徐々に認識されるようになってきました。しかし、まだ認識が十分に広まったとはいえません。その証拠はいたるところに見つかります。試しに、どれでもいいので、サードパーティのAPIを利用している未テストのクラスを選び、そのクラスに対応するユニットテストを書いてみてください。ほぼ間違いなく問題が起きるはずです。問題が起きるのは、おそらくAPIを利用する部分でしょう。糊のついた紙が勝手なところに張り付くような具合に、APIを利用する部分がどうしても邪魔になるのです。APIクラスに「なりすます」方法がないので、自分のコードがAPIクラスとどういうやりとりをしているのかを検出することができず、かつテストに必要なAPIからの戻り値も創りだすことができないのです。

このような状況もいずれ改善されるかもしれません。しかしそのためには、皆がAPIの設計に際し、テストを本当のユースケースの1つとみなすことが必要です。それは残念ながら、単に自分が書いたコードをテストするより難しいことです。「APIを提供するときは、API自身のテストだけでなく、必ずそのAPIを利用するコードのユニットテストも書く」APIの設計者は、これを黄金律にして欲しいと思います。この黄金律を守れば、APIの利用者がユニットテストに際してどのような問題に直面するかを事前に察知できます。 API開発時にこうしておけば、APIのユーザは必ず簡単にユニットテストができる、というような方法はありません。確かにstatic宣言やfinal宣言、sealed宣言といった方法も場合によっては有効です。しかし、重要なのはまずAPIを開発する側が「APIを利用するコードのユニットテストは難しい」と認識することです。それには、自らその難しさを体験するのが一番なのです。一度体験すれば、その後は「テストの難しさ」を設計上の課題の1つととらえ、他の課題と同様に考慮するようになるはずです。