【34】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 つととらえ、他の課題と同様に考慮するようになるはずです。