Das Barbara-Liskov-Substitutionsprinzip (Vor- und Nachbedingungen)

Warum haben so viele Probleme mit diesem Prinzip? Wenn wir nicht "abstrus", sondern eine einfachere Definition nehmen, dann klingt das so:





Die erbende Klasse sollte das Verhalten der Basisklasse ergänzen und nicht überschreiben.





Es klingt klar und ziemlich logisch, wir sind nicht einverstanden. aber verdammt wie kann man das erreichen? Aus irgendeinem Grund überspringen viele Leute einfach Informationen über Vor- und Nachbedingungen , die nur perfekt erklären, was zu tun ist.





In diesem Artikel werden KEINE allgemeinen Beispiele für dieses Prinzip betrachtet, für die es bereits viele Materialien gibt (Beispiel mit einer quadratischen und einer rechteckigen oder Thermostatsteuerung ). Hier werden wir uns etwas eingehender mit Konzepten wie "Voraussetzungen" , "Nachbedingungen" befassen , überlegen, was Kovarianz, Kontravarianz und Invarianz sind und was "historische Zwänge" oder "Regeln der Geschichte" sind.





Voraussetzungen können in einer Unterklasse nicht gestärkt werden

️ Mit anderen Worten, untergeordnete Klassen sollten nicht mehr Voraussetzungen schaffen als in der Basisklasse definiert, um ein gewisses Geschäftsverhalten zu erzielen. Hier ist ein Beispiel:





<?php

class Customer
{
    protected float $account = 0;

    public function putMoneyIntoAccount(int|float $sum): void
    {
        if ($sum < 1) {
            throw new Exception('       1$');
        }

        $this->account += $sum;
    }
}

class  MicroCustomer extends Customer
{
    public function putMoneyIntoAccount(int|float $sum): void
    {
        if ($sum < 1) {
            throw new Exception('       1$');
        }

        //  
        if ($sum > 100) { 
            throw new Exception('      100$');
        }

        $this->account += $sum;
    }
}
      
      



​ . !





«», , .





, , .





, , Bar->process()



, .





<?php

class Foo
{
    public function process(int|float $value)
    {
       // some code
    }
}

class Bar extends Foo
{
    public function process(int|float|string $value)
    {
        // some code
    }
}
      
      



, VIPCustomer



putMoneyIntoAccount



( ) Money



, ( Dollars



).





<?php

class Money {}
class Dollars extends Money {}

class Customer
{
    protected Money $account;

    public function putMoneyIntoAccount(Dollars $sum): void
    {
        $this->account = $sum;
    }
}

class VIPCustomer extends Customer
{
    public function putMoneyIntoAccount(Money $sum): void
    {
        $this->account = $sum;
    }
}
      
      



, , .





​️ , . .





<?php

class Customer
{
    protected Dollars $account;

    public function chargeMoney(Dollars $sum): float
    {
        $result = $this->account - $sum->getAmount();

        if ($result < 0) { // 
            throw new Exception();
        }

        return $result;
    }
}

class  VIPCustomer extends Customer
{
    public function chargeMoney(Dollars $sum): float
    {
        $result = $this->account - $sum->getAmount();

        if ($sum < 1000) { //   
            $result -= 5;  
        }
       
        //    
      
        return $result;
    }
}
      
      



​ , . !





- «», (?!), .





. render()



, JpgImage



, Image



, Renderer



.





<?php

class Image {}
class JpgImage extends Image {}

class Renderer
{
    public function render(): Image
    {
    }
}

class PhotoRenderer extends Renderer
{
    public function render(): JpgImage
    {
    }
}
      
      



​️ . . :)





.





- .





— , . , .





.





<?php 

class Wallet
{
    protected float $amount;
    //        
}
      
      



(« »):





.





, . , . 





<?php

class Deposit
{
    protected float $account = 0;

    public function __construct(float $sum)
    {
        if ($sum < 0) {
            throw new Exception('      ');
        }

        $this->account += $sum;
    }
}

class VipDeposit extends Deposit
{
    public function getMoney(float $sum)
    {
        $this->account -= $sum;
    }
}
      
      



Deposit



. VipDeposit



, account



, Deposit



. .





.





, , , , .





Es ist erwähnenswert, sich zu bemühen, die Vor- / Nachbedingungen loszuwerden. Idealerweise sollten sie als Eingabe- / Ausgabeparameter der Methode definiert werden (z. B. indem vorgefertigte Wertobjekte an die Signatur übergeben und ein bestimmtes gültiges Objekt an die Ausgabe zurückgegeben werden).





Hoffe es war hilfreich.





Quellen von

  1. Wiki - Barbara Liskov Substitutionsprinzip





  2. Metanit





  3. PHP.watch





  4. Telegrammkanal mit kurzen Notizen








All Articles