実装に依存するな、仕様に依存しろ

仕様とはわかりくい概念です。辞書には次のようにあります:

製品や注文にあたって、あらかじめ定める仕上がり品の構造やデザイン。

仕様は発注者が定めて、受注者がそれに従うもののようです。仕様書を固めて、そのとおりに製造し、仕様書と照らし合せて完成品を検品する。一度つくっておしまいという工程であれば、これが全てでしょう。作って動いてで全てであれば仕様書なんて納品されたら燃やしてしまっても問題ないはずです。実際そういうスタンスのいい加減なところだと、仕様書は適当に共有フォルダに放り込まれて、管理もされていないでしょう。

しかし、仕様は実装を行う人や検査を行う人だけが参照するものではありません。仕様は、作る人だけでなく、使う人も従わなければならないものです。仕様として定められていることが満足されることを期待してよい――という約束事が仕様です。つまり、仕様とは利用者と提供者の「契約」です。契約とは双方が守るものであって、一方を縛るものではありません。

利用者は仕様にないことを期待してはなりません。同じ仕様を満たすやり方は幾通りもあります。ここではこれを実装とよびましょう。利用者は仕様に依存するべきであり、実装に依存するべきではありません。利用者は中身の実装なんて知るべきはないし、仮に知っていたとしても知らないふりをして、機能を利用せねばならないのです。このことを「デルメルの法則」と呼びます。

例えば、ある関数 getItem()AbstractItem というインターフェイスをもつオブジェクトを返すという仕様だとして、 ConcreteItem クラスを返す実装になっていたとしましょう。呼び出し側は次のようなことをしてはなりません。

ConcreteItem item = (ConcreteItem) getItem();

これの何が悪いのかというと、getItem()ConcreteItem 以外のものを未来には返すかもしれないからです。今は確かに ConcreteItem だけしか返さないかもしれませんが、そんなことを約束していなければ、他のものを返せれても文句は言えません。非互換だとか言われても、知らんがなとなります。

仕様に違反した使い方をして壊れたら、悪いのは利用者です。実装に依存するのは、ねこを電子レンジに入れるがごとき愚行です。

さすがに上記のような言語仕様で決められているようなものを、わざわざ無視して汚くしてしまう人は少ないですが、見た目に汚くないと人はしばしばこういうことをします。どこからか裏ワザを見つけきて、それを利用して問題を解決しようとするのです。どうも裏ワザを利用して問題を解決するのが、エレガントな解法だと思っているようです。

しかし、このような下手のハッカー気取りは間違っていまして、中身なんてどうでもよいことです。仕様書がないからブラックボックス化しているとしばしば言われますが、仕様書とはブラックボックス化するためにあるものです。利用者が中身に興味を示さないように用意するのが仕様書であるということを我々は理解するべきでしょう。