6

現在、コード コントラクトでは、メンバーが基本クラスで既に前提条件が設定されている派生クラスのメンバーの前提条件を許可していません (現在、エラーではなく警告が表示されます)。この背後にあるロジックがわかりません。派生クラスは、親が期待される場所で常に使用できるようにする必要があるという Liskov の置換規則に関係していることを理解しています。もちろん、「使用済み」とは、期待どおりに動作することを意味します。インターフェイスを実装するさまざまなタイプは状態を追加しないため、契約を正確に義務付けることができるため、これはインターフェイスにとって問題ないように思えます。ただし、基本クラスから継承する場合は、状態と特別な機能を追加するためにそうしているため、オーバーライドするメソッドには追加の要件が必要になることがよくあります。どうして」

以下をご覧ください。

class Speaker
{
    public bool IsPlugged { get; set; }
    protected virtual void Beep()
    {
        Contract.Requires(IsPlugged);
        Console.WriteLine("Beep");
    }
}

class WirelessSpeaker : Speaker
{
    public bool TransmitterIsOn { get; set; }
    protected override void Beep()
    {
        Contract.Requires(TransmitterIsOn);
        base.Beep();
    }
}

このクラス階層はリスコフのルールに違反していると主張するかもしれませんSpeaker. しかし、それがコード コントラクトを使用する理由ではありませんか? 要件が満たされていることを確認するには?

4

3 に答える 3

9

コード コントラクトは、要件を満たすことではなく、要件の伝達に関するものです。の呼び出し元Speaker.Beepは、場合によってのみ有効になるコントラクトに拘束されます。

WirelessSpeaker の機能空間を狭めSpeakerます- それがリスコフの出番です。ワイヤレスであることがわかっSpeakerている場合にのみ、それを効果的に使用できます。その場合、ではなく を明示的に受け入れ、置換の問題を回避する必要があります。WirelessSpeakerSpeaker

コメントに応じて編集:

の作成者はWirelessSpeaker、コマンドの解釈方法を選択しますBeep。このレベルでは表示されるがベース レベルでは表示されない新しいコントラクトを選択すると、s の使用時に 100% 未満の時間に適用される制約が課せられますSpeaker

送信機がオンになっていないときに単にビープ音を鳴らさないとしたら、コード コントラクトについて話していることにはなりません。それらの意図は、実行時に通信することではなく、設計時に呼び出しのセマンティクス(構文だけでなく) を伝達することです。

実行時に例外が発生し、最終的に「不正な」呼び出しが防止されるという事実は、ここではほとんど関係ありません。

于 2014-11-05T19:49:27.863 に答える
1

このような動作の違いが本当に必要な場合は、基本クラスで仮想の「CanBeep」プロパティを公開してから、WirelessSpeaker が TransmitterIsOn を返すように実装することをお勧めします。このようにして、Speaker に契約を入れることができ、Speaker の消費者は、契約上の要件を満たすことができるかどうかを知る方法があります。

とはいえ、変更可能な状態に関連付けられる可能性があるパブリック プロパティは、契約上の要件として最適な選択ではありません。プロパティのチェックとメソッドの呼び出しの間に送信機がオフになるとどうなりますか? 契約の意味をよく考えることが大事だと思います。良い質問は次のとおりです。これは、コンパイル時に静的に証明できる条件ですか、それとも実行時の条件に依存する可能性がありますか? ちなみに、この質問は、静的コントラクト分析ツールを実行することによって最も簡単に答えられます。

于 2014-11-05T19:55:54.873 に答える