Heim > Backend-Entwicklung > PHP-Tutorial > Das Liskov -Substitutionsprinzip

Das Liskov -Substitutionsprinzip

William Shakespeare
Freigeben: 2025-03-01 08:47:09
Original
773 Leute haben es durchsucht

The Liskov Substitution Principle

Kernpunkte

  • Liskov Substitution Principle (LSP) ist ein Schlüsselkonzept für die objektorientierte Programmierung, mit der sichergestellt wird, dass Unterklassen ihre Basisklassenabstraktionen ersetzen können, ohne den Vertrag mit dem Client-Code zu brechen. Es behält die Integrität des Systemdesigns bei und ist entscheidend für die Wiederverwendbarkeit des Codes.
  • Wenn die Methoden in Unterklassen überschreiben, müssen bestimmte Anforderungen erfüllt sein: Ihre Signatur muss mit der Unterschrift der übergeordneten Klasse übereinstimmen.
  • Verstöße gegen
  • lsp können zu unaufhaltsamem Verhalten und Fehlern führen, die schwer zu verfolgen sind. Es macht auch den Code schwieriger zu warten und zu erweitern, da die Annahme, dass eine Unterklasse ihre Superklasse ersetzen kann, nicht mehr wahr ist.
  • Methode -Umschreiben verstößt nicht immer gegen LSP. Wenn die umgeschriebene Methode jedoch das Verhalten der ursprünglichen Methode auf eine Weise ändert, die im Superklassenvertrag nicht erwartet wird, verstößt sie gegen das LSP.
  • Um sicherzustellen, dass der Code LSP entspricht, ist es am besten, Unterklassen zu erstellen, die ihre Basisklassenfunktionen nur erweitern und nicht umschreiben). Zusätzlich kann die Verwendung von Zusammensetzung anstelle von Vererbung und Implementierung von Schnittstellen abgeleitete Klassen erstellen, ohne die durch LSP auferlegte Abstraktion von Bedingungen zu brechen.

Fiktive Szene: Hacker und Matrix

Das folgende Gespräch stammt aus einer Schnittszene aus der Matrix -Trilogie:

Merpheus: Neo, ich bin jetzt in der Matrix. Es tut mir leid, dass ich Ihnen diese schlechten Nachrichten erzählte, aber unser Agent -Tracking -PHP -Programm benötigt ein kurzes Update. Derzeit verwendet die PDO -Methode (mit String) die PDO -Methode (mit String), um den Status aller Matrixagenten aus unserer Datenbank zu erhalten. Wir müssen jedoch stattdessen Vorverarbeitungsabfragen verwenden.

Neo: Klingt gut, Morpheus. Kann ich eine Kopie des Programms erhalten?

Merphes: Kein Problem. Klonen Sie unser Repository und überprüfen Sie die Dateien agentmapper.php und index.php.

(Neo führt einige Git -Befehle aus, und der folgende Code erscheint vor ihm)

<?php namespace ModelMapper;

class AgentMapper
{
    protected $_adapter;
    protected $_table = "agents";

    public function __construct(PDO $adapter) {
        $this->_adapter = $adapter;
    }

    public function findAll() {
        try {
            return $this->_adapter->query("SELECT * FROM " . $this->_table, PDO::FETCH_OBJ);
        }
        catch (Exception $e) {
            return array();
        }
    }   
}
Nach dem Login kopieren
Nach dem Login kopieren
<?php use ModelMapperAgentMapper;

// 一个 PSR-0 兼容的类加载器
require_once __DIR__ . "/Autoloader.php";

$autoloader = new Autoloader();
$autoloader->register();

$adapter = new PDO("mysql:dbname=Nebuchadnezzar", "morpheus", "aa26d7c557296a4e8d49b42c8615233a3443036d");

$agentMapper = new AgentMapper($adapter);
$agents = $agentMapper->findAll();

foreach ($agents as $agent) {
    echo "Name: " . $agent->name .  " - Status: " . $agent->status . "<br>";
}
Nach dem Login kopieren

nio: Morpheus, ich habe gerade die Dokumente bekommen. Ich habe die PDO unterklassifiziert und seine Query () -Methode überschrieben, damit vorverarbeitete Abfragen verwendet werden können. Aufgrund meiner Superkräfte sollte ich diesen Job sehr schnell beenden können. ruhig halten.

(Der Klang der Computertastatur widersetzt sich in der Luft)

nio: Morpheus, die Unterklasse ist zum Testen bereit. Überprüfen Sie es jederzeit.

(Murphys hat schnell auf seinem Laptop gesucht und die folgende Klasse gesehen)

<?php namespace LibraryDatabase;

class PdoAdapter extends PDO
{
    protected $_statement;

    public function __construct($dsn, $username = null, $password = null, array $driverOptions = array()) {
        // 检查是否传递了有效的 DSN
        if (!is_string($dsn) || empty($dsn)) {
            throw new InvalidArgumentException("The DSN must be a non-empty string.");
        }
        try {
            // 尝试创建一个有效的 PDO 对象并设置一些属性。
            parent::__construct($dsn, $username, $password, $driverOptions);
            $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $this->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
        }
        catch (PDOException $e) {
            throw new RunTimeException($e->getMessage());
        }
    }

    public function query($sql, array $parameters = array())
    {
        try {
           $this->_statement = $this->prepare($sql);
           $this->_statement->execute($parameters);
           return $this->_statement->fetchAll(PDO::FETCH_OBJ);        
        }
        catch (PDOException $e) {
            throw new RunTimeException($e->getMessage());
        }
    }
}
Nach dem Login kopieren

Merphes: Der Adapter sieht gut aus. Ich werde es sofort versuchen, um zu sehen, ob unser Agent Mapper aktive Agenten verfolgen kann, die durch die Matrix reisen. Viel Glück für mich.

(Murphys zögerte einen Moment und führte die vorherige Index.php -Datei aus, diesmal mit der Meisterwerks -Pdoadapter -Klasse von Neo. Dann ein Schrei!)

Merpheus: Neo, ich glaube du bist der "Retter"! Aber es gab einen schrecklichen tödlichen Fehler in meinem Gesicht, und die Nachrichten waren wie folgt:

<code>Catchable fatal error: Argument 2 passed to LibraryDatabasePdoAdapter::query() must be an array, integer given, called in path/to/AgentMapper on line (who cares?)</code>
Nach dem Login kopieren

(ein anderer Schrei)

neo: Was ist los? ! Was ist schief gelaufen? ! (mehr Schreie)

Merphes: Ich weiß es wirklich nicht. Oh, Agent Smith kommt jetzt, um mich zu fangen! (Die Kommunikation wurde plötzlich unterbrochen. Die lange Stille beendete das Gespräch, was darauf hindeutet, dass Morpheus unvorbereitet war und von Agent Smith schwer verletzt wurde.)

lsp repräsentiert keine faulen, dummen Programmierer

unnötig zu erwähnen, dass der obige Dialog fiktiv ist, aber das Problem ist zweifellos wahr. Wenn Neo nur ein oder zwei Wissen über das Liskov -Substitutionsprinzip (LSP) wie ein Hacker gelernt hätte, den er einst so berühmt war, konnte Agent Smith sofort verfolgt werden. Am wichtigsten ist, dass Morpheus vor der Bosheit des Wirkstoffs geschützt ist. Es war so schade für ihn. In vielen Fällen denken PHP -Entwickler jedoch fast dasselbe wie die frühere Meinung von Neo: LSP ist nichts anderes als ein puristisches theoretisches Prinzip, das in der Praxis nur wenig Anwendung hat. Aber sie gingen den falschen Weg. Obwohl die formale Definition von LSP schillernd (einschließlich mir) ist, besteht sein Kern darin, eine Hierarchie unklar definierter Klassen zu vermeiden, in denen Nachkommen sehr unterschiedlich sind als Basisklassenabstraktionen, die denselben Vertrag verwenden. Einfach ausgedrückt, legt LSP fest, dass beim Umschreiben von Methoden in Unterklassen die folgenden Anforderungen erfüllt sein müssen:

  1. Seine Signatur muss mit der Signatur der übergeordneten Klasse
  2. übereinstimmen
  3. Die Voraussetzungen (was akzeptiert) müssen gleich oder schwächer sein
  4. ihre Post-Konditionen (was erwartet wird) müssen gleich oder stärker sein
  5. Ausnahme (falls vorhanden) muss mit dem Ausnahmetyp der übergeordneten Klasse
  6. übereinstimmen.

Jetzt können Sie die obige Liste erneut lesen (keine Sorge, ich werde warten) und Sie hoffen zu verstehen, warum dies sinnvoll ist. Wenn Sie zum Beispiel zurückkehren, behält der tödliche Fehler von NEO die Methode -Signatur einfach nicht gleich, wodurch der Vertrag mit dem Client -Code verstößt. Um dieses Problem zu lösen, kann die findAll () -Methode des Agent Mapper mit einigen bedingten Aussagen (offensichtlicher Code -Geruch) umgeschrieben werden, wie unten gezeigt:
<?php namespace ModelMapper;

class AgentMapper
{
    protected $_adapter;
    protected $_table = "agents";

    public function __construct(PDO $adapter) {
        $this->_adapter = $adapter;
    }

    public function findAll() {
        try {
            return $this->_adapter->query("SELECT * FROM " . $this->_table, PDO::FETCH_OBJ);
        }
        catch (Exception $e) {
            return array();
        }
    }   
}
Nach dem Login kopieren
Nach dem Login kopieren

Wenn Sie gut gelaunt sind, probieren Sie die Refactoring -Methode aus und es funktioniert gut, egal ob Sie ein nationales PDO -Objekt oder eine Instanz eines PDO -Adapters verwenden. Ich weiß, dass das rau klingt, aber es ist nur eine schnelle und einfache Lösung und verletzt das Prinzip des Öffnens und Schließens offen. Andererseits kann die Query () -Methode des Adapters so neu gestaltet werden, um der Signatur der Umschreibung der Elternklasse zu entsprechen. Dabei sollten auch alle anderen vom LSP angegebenen Bedingungen erfüllt sein. Kurz gesagt, dies bedeutet, dass das Umschreiben von Methoden mit Vorsicht durchgeführt werden sollte und nur aus sehr starken Gründen durchgeführt werden kann. In vielen Anwendungsfällen ist es unter der Annahme, dass die Schnittstelle nicht verwendet werden kann, eine Unterklasse, die nur ihre Basisklassenfunktionalität erweitert (und nicht überschreibt). Im Fall des PDO -Adapters von NEO funktioniert dieser Ansatz perfekt und wird den Client -Code auf keiner Ebene aufbrechen. Wie ich gerade sagte, gibt es eine effizientere, aber radikalere Lösung, die die Implementierung von Schnittstellen nutzt. Während frühere PDO -Adapter durch Vererbung erstellt wurden und unbestreitbar gegen die LSP -Vorschriften verstoßen, stammen die Nachteile tatsächlich aus der Art und Weise, wie die Agent Mapper -Klasse ursprünglich entworfen wurde. Tatsächlich hängt es von oben nach unten von einer konkreten Datenbankadapter-Implementierung und nicht von einem Schnittstellenvertrag ab. Und große OO -Kraft wurde seit der Antike gesagt, dies ist immer eine schlechte Sache. Wie wird die obige Lösung implementiert?

(Der Rest ähnelt dem Eingabetxt und kann bei Bedarf eingestellt und vereinfacht werden)

Das obige ist der detaillierte Inhalt vonDas Liskov -Substitutionsprinzip. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Neueste Artikel des Autors
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage