Is het afleiden van vierkant uit rechthoek een schending van Liskovs substitutieprincipe?

Ik ben nieuw in het ontwerpen en leren van de ontwerpprincipes.

Er staat dat het afleiden van vierkant uit rechthoek een klassiek voorbeeld is van schending van het vervangingsprincipe van Liskov.

Als dat het geval is, wat zou dan het juiste ontwerp moeten zijn?


Antwoord 1, autoriteit 100%

Het antwoord hangt af van veranderlijkheid. Als je rechthoek- en vierkantklassen onveranderlijk zijn, dan is Squareecht een subtype van Rectangleen is het prima om eerst van tweede af te leiden. Anders zouden Rectangleen Squarebeide een IRectanglezonder mutators kunnen blootleggen, maar het een van het ander afleiden is verkeerd aangezien geen van beide typen een echt subtype is van de ander.


Antwoord 2, autoriteit 76%

Ik geloof dat de redenering ongeveer als volgt is:

Stel dat je een methode hebt die een rechthoek accepteert en de breedte aanpast:

public void SetWidth(Rectangle rect, int width)
{
    rect.Width = width;
}

Het zou volkomen redelijk moeten zijn, gezien wat een rechthoek is, om aan te nemen dat deze test zou slagen:

Rectangle rect = new Rectangle(50, 20); // width, height
SetWidth(rect, 100);
Assert.AreEqual(20, rect.Height);

… omdat het wijzigen van de breedte van een rechthoek geen invloed heeft op de hoogte.

Stel echter dat u een nieuwe klasse Square hebt afgeleid van Rectangle. Een vierkant heeft per definitie hoogte en breedte altijd gelijk. Laten we die test opnieuw proberen:

Rectangle rect = new Square(20); // both width and height
SetWidth(rect, 100);
Assert.AreEqual(20, rect.Height);

Die test zal mislukken, omdat het instellen van de breedte van een vierkant op 100 ook de hoogte zal veranderen.

Het substitutieprincipe van Liskov wordt dus geschonden door Vierkant af te leiden uit Rechthoek.

De “is-a”-regel is logisch in de “echte wereld” (een vierkant is zeker een soort rechthoek), maar niet altijd in de wereld van softwareontwerp.

Bewerken

Om uw vraag te beantwoorden, zou het juiste ontwerp waarschijnlijk moeten zijn dat zowel Rechthoek als Vierkant afkomstig zijn van een algemene “Polygon”- of “Vorm”-klasse, die geen regels oplegt met betrekking tot breedte of hoogte.


Antwoord 3, autoriteit 7%

Ik ben het er niet mee eens dat het afleiden van vierkant uit rechthoek noodzakelijkerwijs LSP schendt.

In het voorbeeld van Matt, als je code hebt die afhankelijk is van breedte en hoogte die onafhankelijk zijn, dan schendt deze in feite LSP.

Als je echter overal in je code een vierkant kunt vervangen door een vierkant zonder aannames te breken, schend je LSP niet.

Het komt er dus echt op neer wat de abstractierechthoek betekent in uwoplossing.


Antwoord 4, autoriteit 4%

Ik heb de laatste tijd veel met dit probleem geworsteld en dacht dat ik mijn hoed aan de ring kon toevoegen:

public class Rectangle {
    protected int height;    
    protected int width;
    public Rectangle (int height, int width) {
        this.height = height;
        this.width = width;
    }
    public int computeArea () { return this.height * this.width; }
    public int getHeight () { return this.height; }
    public int getWidth () { return this.width; }
}
public class Square extends Rectangle {
    public Square (int sideLength) {
        super(sideLength, sideLength);
    }
}
public class ResizableRectangle extends Rectangle {
    public ResizableRectangle (int height, int width) {
        super(height, width);
    }
    public void setHeight (int height) { this.height = height; }
    public void setWidth (int width) { this.width = width; }
}

Let op de laatste klasse, ResizableRectangle. Door de “resizabiliteit” in een subklasse te verplaatsen, krijgen we codehergebruik tijdens het verbeteren van ons model. Denk eraan als dit: een vierkant kan niet vrij worden verkleind tijdens het resteren van een vierkant, terwijl niet-vierkante rechthoeken kan. Niet Alle -rechthoeken kunnen echter worden aangepast, omdat een vierkant een rechthoek is (en het kan niet vrij worden verkleind met het behouden van de “identiteit”). (O_O), dus het is logisch om een ​​basis te maken Rectangleklasse die niet is, omdat dit een extra woning is van wat rechthoeken.


Antwoord 5, Autoriteit 3%

Het probleem is dat wat wordt beschreven echt geen “type” maar een cumulatief opkomende eigendom is.

Alles wat je echt hebt is een vierhoek en dat zowel “kwadriezing” als “rechthoekigheid” gewoon opkomende artefacten zijn afgeleid van eigenschappen van de hoeken en zijkanten.

Het volledige concept van “vierkant” (of zelfs rechthoek) is slechts een abstracte weergave van een verzameling eigenschappen van het object in verband met elkaar en het betreffende object, niet het type object in en van zichzelf.

Dit is waar het denken aan het probleem in de context van een typeloze taal kan helpen, omdat het niet het type is dat bepaalt of het “een vierkant” is, maar de werkelijke eigenschappen van het object dat bepaalt of het “een vierkant” is.

Ik denk dat als je de abstractie nog verder wilt nemen, je zou niet eens zeggen dat je een vierhoek hebt, maar dat je een polygoon of zelfs een vorm hebt.


Antwoord 6, Autoriteit 2%

Laten we aannemen dat we de klasse rechthoek hebben met de twee (voor eenvoudige publieke) eigenschappenbreedte, hoogte. We kunnen die twee eigenschappen wijzigen: R.WIDTH = 1, R.HEIGHT = 2.
Nu zeggen we een vierkant is_a-rechthoek. Maar hoewel de claim is “een vierkant zal zich gedragen als een rechthoek” We kunnen niet instellen .width = 1 en .Height = 2 op een vierkant object (uw klasse past de breedte waarschijnlijk aan als u de hoogte en vice versa instelt). Er is dus ten minste één geval waar een object van het type vierkant zich niet gedraagt ​​als een rechthoek en daarom kunt u ze (volledig) niet vervangen.


Antwoord 7, Autoriteit 2%

Ik ben van mening dat OOD / OOP-technieken bestaan ​​om software in te schakelen om de echte wereld te vertegenwoordigen. In de echte wereld is een vierkant een rechthoek met gelijke kanten. Het plein is alleen een vierkant omdat het gelijke zijden heeft, niet omdat het besloot een plein te zijn. Daarom moet het OO-programma ermee omgaan. Natuurlijk, als de routine het instelling van het object wil dat het vierkant is, kan het de Lengte-eigenschap en het woninghuis opgeven zoals gelijk aan hetzelfde bedrag. Als het programma met behulp van het object later moet weten als het vierkant is, hoeft het alleen maar te vragen. Het object kan een alleen-lezen Booleaanse eigenschap hebben genaamd “Square”. Wanneer de routine het roept, kan het object terugkeren (lengte = breedte). Dit kan nu het geval zijn, zelfs als het rechthoek-object onveranderlijk is. Als de rechthoek inderdaad onveranderlijk is, kan de waarde van de vierkante woning in de constructeur worden ingesteld en hiermee gedaan. Waarom is dit dan een probleem? De LSP vereist subobjecten om onveranderlijk te zijn om toe te passen en dat het een subobject van een rechthoek is, wordt vaak gebruikt als een voorbeeld van zijn overtreding. Maar dat lijkt geen goed ontwerp te zijn, want wanneer het gebruik van routine het object roept als “objsquare”, moet het in de innerlijke detail weten. Zou het niet beter zijn als het niet schat, of de rechthoek vierkant was of niet? En dat zou zijn omdat de methoden van de rechthoek goed zouden zijn, ongeacht. Is er een beter voorbeeld van wanneer de LSP wordt geschonden?

Nog een vraag: Hoe is een object onveranderlijk gemaakt? Is er een “onveranderlijke” eigenschap die kan worden ingesteld op instantiation?

Ik heb het antwoord gevonden en het is wat ik verwachtte. Omdat ik een VB-ontwikkelaar ben, is dat waar ik in geïnteresseerd ben. Maar de concepten zijn hetzelfde in de talen. In VB .NET creëert u onveranderlijke klassen door de alleen-lezingen te maken en gebruikt u de nieuwe constructor om de instantieeringsroutine toe te staan ​​om onroerendgoedwaarden op te geven wanneer het object is aangemaakt. U kunt ook constanten gebruiken voor enkele van de eigenschappen en ze zullen altijd hetzelfde zijn. Van het maken van het voorwerp is het object onveranderlijk.


Antwoord 8

Het is vrij eenvoudig 🙂 Hoe meer ‘base’ de klasse (de eerste in de afleidingsketen) zou de meest algemene moeten zijn.

Bijvoorbeeld vorm -> Rechthoek -> Vierkant.

Hier is een vierkant een speciaal geval van een rechthoek (met beperkte afmetingen) en een rechthoek is een speciaal geval van een vorm.

Anders gezegd: gebruik de “is een” test. Een schildknaap is een rechthoek. Maar een rechthoek is niet altijd een vierkant.

Other episodes