Untersuchung des Liskov-Substitutionsprinzips

Der Begriff SOLID ist ein beliebtes Akronym, das sich auf fünf Prinzipien der Softwarearchitektur bezieht. Dazu gehören: SRP (Single Responsibility), Öffnen / Schließen, Liskovs Substitution, Schnittstellentrennung und Abhängigkeitsinversion.

LSP (Liskov Substitution Principle) ist ein Grundprinzip von OOP und besagt, dass abgeleitete Klassen in der Lage sein sollten, ihre Basisklassen zu erweitern, ohne ihr Verhalten zu ändern. Mit anderen Worten, abgeleitete Klassen sollten durch ihre Basistypen ersetzt werden können, dh ein Verweis auf eine Basisklasse sollte durch eine abgeleitete Klasse ersetzt werden können, ohne das Verhalten zu beeinflussen. Das Liskov-Substitutionsprinzip stellt eine starke Verhaltensuntertypisierung dar und wurde von Barbara Liskov im Jahr 1987 eingeführt.

Laut Barbara Liskov "ist hier etwa die folgende Substitutionseigenschaft erwünscht: Wenn für jedes Objekt o1 vom Typ S ein Objekt o2 vom Typ T vorhanden ist, so dass für alle in Bezug auf T definierten Programme P das Verhalten von P gilt ist unverändert, wenn o2 durch o1 ersetzt wird, dann ist S ein Subtyp von T. "

Ein klassisches Beispiel für einen Verstoß gegen das Liskov-Substitutionsprinzip ist das Rechteck-Quadrat-Problem. Die Square-Klasse erweitert die Rectangle-Klasse und geht davon aus, dass Breite und Höhe gleich sind.

Betrachten Sie die folgende Klasse. Die Rectangle-Klasse enthält zwei Datenelemente - width und height. Es gibt auch drei Eigenschaften - Höhe, Breite und Fläche. Während die ersten beiden Eigenschaften die Höhe und Breite des Rechtecks ​​festlegen, verfügt die Area-Eigenschaft über einen Getter, der den Bereich des Rechtecks ​​zurückgibt.

 class Rectangle

    {

        protected int width;

        protected int height;

         public virtual int Width

        {

            get

            {

                return width;

            }

            set

            {

                width = value;

            }

        }

 

        public virtual int Height

        {

            get

            {

                return height;

            }

            set

            {

                height = value;

            }

        }

               

       public int Area

        {

            get

            {

                return height * width;

            }

         }    

    }

Ein Quadrat ist eine Art Rechteck, dessen Seiten alle gleich groß sind, dh die Breite und Höhe eines Quadrats sind gleich.

class Square : Rectangle

    {

        public override int Width

        {

            get

            {

                return width;

            }

            set

            {

                width = value;

                height = value;

            }

        }

        public override int Height

        {

            get

            {

                return width;

            }

            set

            {

                width = value;

                height = value;

            }

        }

    }

 Betrachten Sie eine andere Klasse namens ObjectFactory.

 class ObjectFactory

    {

        public static Rectangle GetRectangleInstance()

        {

            return new Square();

        }

    }

Beachten Sie, dass die Setter für die Eigenschaften Breite und Höhe in der Klasse Quadrat überschrieben und geändert wurden, um sicherzustellen, dass Höhe und Breite identisch sind. Lassen Sie uns nun eine Instanz der Rectangle-Klasse erstellen und ihre Eigenschaften für Höhe und Breite festlegen.

Rectangle s = ObjectFactory.GetRectangleInstance();

s.Height = 9;

s.Width = 8;

Console.WriteLine(s.Area);

Das obige Codefragment würde bei Ausführung den Wert 64 in der Konsole anzeigen. Der erwartete Wert beträgt 72, da die angegebene Breite und Höhe 9 bzw. 8 beträgt. Dies ist ein Verstoß gegen das Liskov-Substitutionsprinzip. Dies liegt daran, dass die Square-Klasse, die die Rectangle-Klasse erweitert hat, das Verhalten geändert hat. Um sicherzustellen, dass das Liskov-Substitutionsprinzip nicht verletzt wird, kann die Square-Klasse die Rectangle-Klasse erweitern, sollte jedoch das Verhalten nicht ändern. Das Verhalten wurde geändert, indem die Setter sowohl für die Eigenschaften Breite als auch Höhe geändert wurden. Die Werte für Höhe und Breite sind gleich, wenn es sich um ein Quadrat handelt. Sie sollten nicht gleich sein, wenn es sich um ein Rechteck handelt.

Wie können wir das beheben, dh sicherstellen, dass dieses Prinzip nicht verletzt wird? Nun, Sie können eine neue Klasse namens Viereck einführen lassen und sicherstellen, dass sowohl die Rechteck- als auch die Quadratklasse die Viereckklasse erweitern.

 public class Quadrilateral

    {

        public virtual int Height { get; set; }

        public virtual int Width { get; set; }

        public int Area

        {

            get

            {

                return Height * Width;

            }

        }

    } 

Jetzt sollten sowohl die Rechteck- als auch die Quadratklasse die Viereckklasse erweitern und die Werte der Eigenschaften Breite und Höhe entsprechend festlegen. Im Wesentlichen sollten die abgeleiteten Klassen über die erforderliche Funktionalität verfügen, um Werte für diese Eigenschaften basierend auf dem Typ der viereckigen Instanz festzulegen, für die Sie die Fläche berechnen müssen. Beachten Sie, dass sowohl die Eigenschaften Höhe als auch Breite in der viereckigen Klasse als virtuell markiert wurden. Dies bedeutet, dass diese Eigenschaften von den Klassen überschrieben werden sollten, die die viereckige Klasse ableiten.

Das Liskov-Substitutionsprinzip ist eine Erweiterung des Open-Close-Prinzips und wird verletzt, wenn Sie Code geschrieben haben, der "nicht implementierte Ausnahmen" auslöst, oder Methoden in einer abgeleiteten Klasse ausblenden, die in der Basisklasse als virtuell markiert wurden. Wenn Ihr Code dem Liskov-Substitutionsprinzip entspricht, haben Sie viele Vorteile. Dazu gehören: Wiederverwendbarkeit des Codes, reduzierte Kopplung und einfachere Wartung.