1)LSPはインターフェイスにも適用されますか?つまり、特定のインターフェイスを実装するクラスを使用しても、期待される動作を得ることができるはずですか?
2)それが実際に当てはまる場合、継承を使用することに対する主な理由の1つがLSPに準拠していますか?おそらく:
a)緩い結合の利点は、LSPに準拠しないリスクを上回ります
b)継承と比較して、クラス(インターフェースを実装する)がLSPに準拠しない可能性ははるかに小さい
ありがとうございました
1)LSPはインターフェイスにも適用されますか?つまり、特定のインターフェイスを実装するクラスを使用しても、期待される動作を得ることができるはずですか?
2)それが実際に当てはまる場合、継承を使用することに対する主な理由の1つがLSPに準拠していますか?おそらく:
a)緩い結合の利点は、LSPに準拠しないリスクを上回ります
b)継承と比較して、クラス(インターフェースを実装する)がLSPに準拠しない可能性ははるかに小さい
ありがとうございました
LSPはインターフェイスにも適用されますか?つまり、特定のインターフェイスを実装するクラスを使用しても、期待される動作を得ることができるはずですか?
LSPが契約に適用されます。コントラクトは、クラスまたはインターフェースの場合があります。
それが実際に当てはまる場合、継承を使用することに対する主な理由の1つが、 LSP?おそらく:
それはインターフェースやクラスについてではありません。それは契約違反についてです。(または)Break()
にメソッドがあるとしましょう。それを呼ぶ人は誰でも車両が壊れることを期待するでしょう。実装の1つが壊れなかった場合の驚きを想像してみてください。それがLSPのすべてです。IVehicle
VehicleBase
a)緩い結合の利点は、LSPに準拠しないリスクを上回ります
え?
b)継承と比較して、クラス(インターフェースを実装する)がLSPに準拠しない可能性ははるかに小さい
え?
原理をよりよく理解するために、私のSOLIDの記事を読むことをお勧めします:http://blog.gauffin.org/2012/05/solid-principles-with-real-world-examples/
アップデート
詳述すると、継承を使用すると、仮想メソッドはプライベートメンバーを消費する可能性があり、これらの仮想メソッドをオーバーライドするサブクラスはアクセスできません。
はい。それは良い。メンバー(フィールド)は常に保護する必要があります(=プライベートとして宣言)。それらを定義したクラスだけが、それらの値がどうあるべきかを本当に知っています。
また、派生クラスは親からコンテキストを継承するため、将来の親クラスの変更などによって破損する可能性があります。
これは、オープン/クローズド原則の違反です。つまり、動作を変更することでクラスコントラクトが変更されます。クラスは拡張する必要があり、変更しないでください。もちろん、常に可能というわけではありませんが、変更によってクラスの動作が変わることはありません(バグ修正を除く)。
したがって、インターフェイスを実装するクラスにコントラクトを尊重させるよりも、サブクラスにコントラクトを尊重させる方が難しいと感じています。
継承による拡張が難しいのには、一般的な理由があります。これは、関係が真のis-a
関係ではなく、開発者が基本クラスの機能を利用したいだけだからです。
それは間違っている。その場合は、コンポジションを使用することをお勧めします。
現在、コードプラクティスをLSPと比較/対照しています。期待と、何を期待すべきかをどう定義するかを決めることがすべてだと思います。代用が常に同じことを意味するとは思いません。たとえば、私が定義した場合
interface ICalculation
{
double Calculate(double A, double B);
}
、、、を定義する意図でclass Add : ICalculation
、class Subtract : ICalculation
これらのクラスはそれぞれを実行する必要があると思いますが、すべて同じ単体テストに合格する必要があるとは思いませんclass Multiply : ICalculation
。私は拡張性のためにインターフェースを使用するのが好きです。そこでは、各拡張機能が異なる機能を持っています。車のブレーキの場合、私はクラスにインターフェースIBrakableを与え、これを行います。 class Divide : ICalculation
Calculation(A, B)
var myBrakableCar = MyCar as IBrakable;
if(myBrakableCar != null)
{
myBrakableCar.Brake();
}
クラスはその使用において予測可能であるだけでなく、有用である必要があると私は信じています。便利なものが一番上に来ます。
新しいコンセプトである「RespectiveSubstition」を作成します。これは、缶に書かれていることを実行します。
広告1):はい。ただし、インターフェイスにコントラクトを適用させることは困難であり、実際に実行する必要があります。
Ad 2):インターフェースに対するプログラミングはトレードオフです。ご指摘のとおり、インターフェースは、重要な契約を持つすべてのインターフェースのLSP違反を助長します。
これらはあなたの質問に対する答えであると私は信じています、あるいは少なくとも、これらはインターフェースの奨励された使用に挑戦する未回答の質問であるという私の認識です。
私はTDDを行うのが好きなので、(C#で)常にインターフェイスを使用しています。次に、モックと対応する実装がLSPに違反しないことを願っています。なぜなら、それらが実行されると、テストスイートはもはや健全ではないからです(何かが機能すると主張しますが、そうではありません)。
ときどき、インターフェイスの代わりに抽象基本クラスを作成します。抽象基本クラスは単にコントラクトを適用し、実際の実装を定義するか、単体テスト中にモックされることになっている仮想保護メソッドに委任します。これは、単体テスト自体のエラーが発見される一部のアプリケーションで役立つことが証明されています。
しかし、その後、C#での多重継承のサポートが不足していることに遭遇し、とにかくインターフェイスで立ち往生しています(または、このアプローチを選択した場合は、SRPの背後に隠れることができます)