事前条件を弱める(アサーションをアスペクト&メタデータで実装)
事前・事後条件と継承について,「オブジェクト指向入門(isbn:4756100503)」では次のように解説されています.たぶん(本が手元にないのでうろ覚え).
- 派生クラスは事前条件を弱めることができる.
- 派生クラスは事後条件を強めることができる.
例えばあるメソッドの事前条件に
size < 100
とあった場合,その派生クラスでは
size < 200
とすることはできますが,
size < 50
としてはいけません.
同じく,あるメソッドの事後条件に
result < 100
とあった場合,その派生クラスでは
result < 50
とすることはできますが,
result < 200
としてはいけません.
Liskov Substitution Principle に反するからですね(まさーるさん元気かなぁ?)
ここで,派生クラスの事後条件が基底クラスの事前条件を強めるというのは容易に実装できます.全ての事後条件の and
をとればいいだけです.たぶん.
しかし,派生クラスの事前条件が基底クラスの事前条件を弱めるというのは,なかなか大変そう.
例えば基底クラスの事前条件が b1,b2
と二つあり,派生クラスが b1
を弱めた事前条件 d1
を持つ場合,(b1 and b2) or d1
とかではなくて,(d1 and b2)
または (b1 or d1) and b2
にしなくてはならないわけですが,事前条件の式だけを見て d1
が b1
を弱くしたものだと判断するのはちょっと無理っぽい.
Eiffelだと require else
とか書くと d1 or (b1 and b2)
になるのかなぁ? ちょっとよく分かりません.早くOOSC2の翻訳が欲しい... 無念だ.
しょうがないので,事前条件には名前を付けてもらうことにしよう.そして,同じ名前の事前条件が派生クラスにあった場合,それは基底クラスの事前条件を弱めたものに違いないと決めつけることにしよう.
仮に,派生クラスの事前条件 d1
が基底クラスの事前条件 b1
を弱めたものだとするなら,派生クラスの事前条件を検証する際に b1
はチェックする必要がないはずです.ですよね?
しかし,そういう実装でいいのでしょうか? もし,d1
の記述が間違っていて,完全に b1
の成立条件を包含していなかったら? と考えると,ちゃんと d1
と b1
の or
をとる方が無難な感じがします.
そうすると,基底クラスで
flag == FLAG_A || flag == FLAG_B
なんていう事前条件があり,派生クラスではflagがFAG_Cも受け付けられるという場合に
flag == FLAG_A || flag == FLAG_B || flag == FLAG_C
と書かなくても
flag == FLAG_C
で済んだりするのですが,それはどうなんでしょうか? 不明瞭になるのでよくない気もします.
まぁいっか.わかりやすくしたければ,ちゃんと書けばいいだけだし.
ということで,アサーション&メタデータによるアスペクトの実装では,事前条件は
* @@Require("size", "size < 100")
みたいに名前と条件を書くことにしよう.
そして,名前が同じものは or
で,名前が異なるものは and
でつなぐことにします.