私はここ数日、リスコフの置換原則を理解しようとしてきました。非常に典型的な長方形/正方形の例でいくつかのコードテストを行っているときに、以下のコードを作成し、それについて2つの質問を思いつきました。
質問1:スーパークラスとサブクラスの関係がある場合、インスタンスをスーパータイプとして宣言し、サブタイプとしてインスタンス化(新規作成)するのはなぜですか?
インターフェイスを介してポリモーフィズムを実行している場合、変数を次のように宣言してインスタンス化する理由を理解しています。
IAnimal dog = new Dog();
しかし、古いプログラミングクラスといくつかのブログの例でそれを思い出したので、継承を通じてポリモーフィズムを使用するとき、いくつかのコードがこのように変数を宣言するいくつかの例をまだ見ています
Animal dog = new Dog();
以下の私のコードでは、SquareはRectangleを継承しているため、次のように新しいSquareインスタンスを作成すると次のようになります。
Square sq = new Square();
それでも長方形として扱うことも、一般的な長方形のリストに追加することもできます。それでは、なぜ誰かがそれをRectangle = new Square()として宣言したいのでしょうか。私が見ていない利点、またはこれが必要となるシナリオはありますか?私が言ったように、以下の私のコードはうまく機能します。
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
var rect = new Rectangle(300, 150);
var sq = new Square(100);
Rectangle liskov = new Square(50);
var list = new List<Rectangle> {rect, sq, liskov};
foreach(Rectangle r in list)
{
r.SetWidth(90);
r.SetHeight(80);
r.PrintSize();
r.PrintMyType();
Console.WriteLine("-----");
}
Console.ReadLine();
}
public class Rectangle
{
protected int _width;
protected int _height;
public Rectangle(int width, int height)
{
_width = width;
_height = height;
}
public void PrintMyType()
{
Console.WriteLine(this.GetType());
}
public void PrintSize()
{
Console.WriteLine(string.Format("Width: {0}, Height: {1}", _width, _height));
}
public virtual void SetWidth(int value)
{
_width = value;
}
public virtual void SetHeight(int value)
{
_height = value;
}
public int Width { get { return _width; } }
public int Height { get { return _height; } }
}
public class Square : Rectangle
{
public Square(int size) : base(size, size) {}
public override void SetWidth(int value)
{
base.SetWidth(value);
base.SetHeight(value);
}
public override void SetHeight(int value)
{
base.SetHeight(value);
base.SetWidth(value);
}
}
}
}
これはリスコフの置換原則に違反しているはずですが、次の出力が得られます。
"幅:90、高さ:80
ConsoleApp.Program + Rectangle
幅:80、高さ:80
ConsoleApp.Program + Square
幅:80、高さ:80 ConsoleApp.Program + Square
質問2:では、なぜ、またはどのようにこのコードサンプルがLSPを破壊しているのでしょうか。すべての辺の正方形の不変量が等しいために、辺が独立して変更できるのは長方形の不変量を壊すだけですか?それが理由である場合、LSP違反は理論上のものにすぎませんか?または、コードで、このコードが原則を破っているのをどのように見ることができますか?
編集:私が読んでいたLSPブログ記事の1つで出てきた3番目の質問を思いついたが、答えがなかったのでこれだ
質問3:オープンクローズの原則は、新しいクラス(継承またはインターフェース)を通じて新しい動作/機能を導入する必要があると述べています。たとえば、基本クラスにWriteLogメソッドがあり、前提条件はありませんが、メソッドをオーバーライドする新しいサブクラスを導入しますが、イベントが非常に重要な場合にのみ実際にログに書き込みます。新しい意図された機能(サブタイプで強化されている前提条件)、それでもLSPを壊しているでしょうか?この場合、2つの原則は互いに矛盾しているように見えます。
前もって感謝します。