昨日、仕様の話をしたのだけど、もう少し書いておこうかと。この話題になると長くなりがちですね、ヤッパリ。
昨日より:
とまー、こういう仕様の計算が自由にできたらいいな、と思っているのです。
けれども、
まー、難しいですね、イロイロと。
なんです。
「難しい」という話だけだとつまらないので、楽しそうなこと(?)や少しは現実的な話をすることにします。
実際には1人だけで作業するにしても、仕様を書く(設計)、実装する、テストするという3つの立場、役割は区別する必要があります。そうしないと、仕様技術の意義は全然理解できないでしょう。
以下では、仕様を書く人(役割)はあまり考えないで、実装者とテスターについて考えます。-- すごく大事な前提があります。
テスターと実装者のお約束:
- 実装者は、仕様に書いてあることは守る。だが、仕様に書いてないことはどのように実装してもよい。
- テスターは、仕様に書いてあることと明白に違う動作にはクレームをつける。だが、仕様に書いてないことでトヤカク言ってはいけない。
つまり、実装者とテスターは、共通の仕様記述以外の手段では情報交換しないことになります。
が、実際には、実装者とテスターの直接コミュニケーションを断っても、それでも、暗黙の情報が流れてしまいます。
例えば、昨日のエントリーで僕は、Counter, value(), inc()のような名前を使いました。これを見て、「incはインクリメント(increement)のことで、カウンタの値を1増やすのだろう」とだいたいの人は思うわけです。つまり、名前を通じて情報が勝手に流れます。
このことは、名前の付け方が大事であるという教訓を導くと同時に、名前やコメントの印象が「実装とテスト」の役割を曖昧にしてしまう危険性も示唆<しさ>します。
例えば、次の2つの記述文(制約)しかない状況を考えます。
// 制約1
on (value() < 100) receiving {inc();}
emits {valueChanged();}
// 制約2
on (value() >= 100) receiving {inc();}
emits {overflow();}
メッセージ受発信はメソッド呼び出しだとして、次の実装は完全に仕様を満たします。
int value() {
return 1;
}void inc() {
valueChanged();
}
制約1に従って、inc()が来たらvalueChanged()を発行してます。この実装では、(value() >= 100)という条件が満たされることは絶対にないので、制約2を根拠にクレームされることはあり得ません。つまり、テスターがどんなテストをしようと、クレームを付けられません(完璧!)。
フォーマルな立場からは、「もっと精密に仕様を記述せよ」ということになりますが、そればっかり言うと、仕様技術を誰も使わなくなります。現実的には:
となるでしょうか。
今の例では、第3の仕様記述文として「inc()の後では、value()が返す値が1だけ増える」という自然言語文を加えます。すると、いつでも1ばっかり返している当該の実装はダメということになります。
(続く、が、いつになるかわからない)