Rumah > pembangunan bahagian belakang > tutorial php > Prinsip penggantian Liskov

Prinsip penggantian Liskov

William Shakespeare
Lepaskan: 2025-03-01 08:47:09
asal
773 orang telah melayarinya

The Liskov Substitution Principle

mata teras

    Prinsip Penggantian Liskov (LSP) adalah konsep utama dalam pengaturcaraan berorientasikan objek yang memastikan subkelas dapat menggantikan abstrak kelas asas mereka tanpa melanggar kontrak dengan kod klien. Ia mengekalkan integriti reka bentuk sistem dan penting untuk kebolehgunaan semula kod.
  • Apabila kaedah penggantian dalam subclass, keperluan tertentu mesti dipenuhi: tandatangan mereka mesti sepadan dengan tandatangan kelas induk;
  • Pelanggaran LSP boleh membawa kepada tingkah laku dan kesilapan yang tidak dapat dihalang yang sukar untuk dijejaki. Ia juga menjadikan kod lebih sukar untuk mengekalkan dan memperluaskan, kerana andaian bahawa subclass boleh menggantikan superclassnya tidak lagi benar.
  • Kaedah penulisan semula tidak selalu melanggar LSP. Walau bagaimanapun, jika kaedah yang ditulis semula mengubah tingkah laku kaedah asal dengan cara yang tidak dijangka dalam kontrak superclass, ia akan melanggar LSP.
  • Untuk memastikan bahawa kod itu mematuhi LSP, sebaiknya membuat subkelas yang hanya melanjutkan (bukan menulis semula) fungsi kelas asas mereka. Di samping itu, menggunakan komposisi dan bukannya warisan dan melaksanakan antara muka boleh membantu mewujudkan kelas yang diperoleh tanpa melanggar abstraksi keadaan yang dikenakan oleh LSP.

adegan fiksyen: penggodam dan matriks

Perbualan berikut datang dari adegan potong dari trilogi Matrix:

Merpheus: Neo, saya dalam matriks sekarang. Maaf untuk memberitahu anda berita buruk ini, tetapi ejen kami menjejaki program PHP memerlukan kemas kini yang cepat. Ia kini menggunakan kaedah pertanyaan PDO () (dengan rentetan) untuk mendapatkan keadaan semua ejen matriks dari pangkalan data kami, tetapi kami perlu menggunakan pertanyaan preprocessing sebaliknya.

Neo: Bunyi baik, Morpheus. Bolehkah saya mendapatkan salinan program?

Merphes: Tiada masalah. Klon repositori kami dan periksa fail AgentMapper.php dan index.php.

(Neo melaksanakan beberapa arahan git, dan kod berikut muncul di hadapannya)

<?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();
        }
    }   
}
Salin selepas log masuk
Salin selepas log masuk
<?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>";
}
Salin selepas log masuk
nio: Morpheus, saya hanya mendapat dokumen. Saya subclassed PDO dan mengatasi kaedah pertanyaannya () supaya ia boleh menggunakan pertanyaan yang telah diproses. Oleh kerana kuasa besar saya, saya sepatutnya dapat menyelesaikan pekerjaan ini dengan cepat. Tetap tenang.

(bunyi papan kekunci komputer bergema di udara)

nio: Morpheus, subclass bersedia untuk ujian. Semaknya bila -bila masa.

(Murphys mencari dengan cepat di komputer ribanya dan melihat kelas berikut)

<?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());
        }
    }
}
Salin selepas log masuk
Merphes: Penyesuai kelihatan baik. Saya akan mencubanya segera untuk melihat sama ada Mapper Ejen kami dapat mengesan ejen aktif yang bergerak melalui Matrix. Semoga berjaya.

(Murphys teragak -agak seketika, menjalankan fail index.php sebelumnya, kali ini menggunakan kelas karya PDOADAPTER NEO. Kemudian, jeritan!) Merpheus: Neo, saya percaya anda adalah "Juruselamat"! Tetapi ada kesilapan maut yang dahsyat di wajah saya, dan berita itu adalah seperti berikut:

(satu lagi jeritan)
<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>
Salin selepas log masuk

neo: Apa yang salah? ! Apa yang salah? ! (lebih banyak jeritan)

Merphes: Saya tidak tahu. Oh, Agent Smith akan datang untuk menangkap saya sekarang! (komunikasi tiba -tiba terganggu. Keheningan yang panjang mengakhiri perbualan, menunjukkan bahawa Morpheus ditangkap dan cedera parah oleh Agent Smith.)

LSP tidak mewakili pengatur malas, bodoh

tidak perlu dikatakan, dialog di atas adalah fiksyen, tetapi masalahnya sudah pasti benar. Sekiranya Neo hanya belajar satu atau dua keping pengetahuan tentang Prinsip Penggantian Liskov (LSP) seperti penggodam yang pernah dia terkenal seperti yang pernah dia lakukan, Agent Smith dapat dikesan dengan segera. Paling penting, Morpheus dilindungi dari kebencian ejen. Sayang sekali untuknya. Walau bagaimanapun, dalam banyak kes, pemaju PHP berfikir tentang LSP hampir sama dengan pendapat sebelumnya Neo: LSP hanyalah prinsip teoretikal purist yang mempunyai sedikit permohonan dalam amalan. Tetapi mereka pergi dengan cara yang salah. Walaupun definisi formal LSP mempesonakan (termasuk saya), terasnya adalah untuk mengelakkan hierarki kelas yang tidak jelas di mana keturunan berkelakuan sangat berbeza dari abstrak kelas asas yang menggunakan kontrak yang sama. Ringkasnya, LSP menetapkan bahawa apabila menulis semula kaedah dalam subkelas, keperluan berikut mesti dipenuhi:

tandatangannya mesti sepadan dengan tandatangan kelas induk
  1. prasyarat (apa yang diterima) mestilah sama atau lemah
  2. post-conditions mereka (apa yang diharapkan) mestilah sama atau lebih kuat
  3. Pengecualian (jika ada) mestilah sama dengan jenis pengecualian yang dibuang oleh kelas induk
  4. Sekarang, jangan ragu untuk membaca senarai di atas sekali lagi (jangan risau, saya akan tunggu) dan anda berharap dapat memahami mengapa ini masuk akal. Kembali ke contoh, kesilapan maut Neo hanya gagal untuk menyimpan tanda tangan yang sama, dengan itu melanggar kontrak dengan kod klien. Untuk menyelesaikan masalah ini, kaedah FindAll () pemetaan ejen boleh ditulis semula dengan beberapa pernyataan bersyarat (bau kod jelas), seperti yang ditunjukkan di bawah:
<?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();
        }
    }   
}
Salin selepas log masuk
Salin selepas log masuk

Jika anda berada dalam mood yang baik, cuba kaedah refactoring dan ia akan berfungsi dengan baik, sama ada menggunakan objek PDO asli atau contoh penyesuai PDO. Saya tahu ini kedengarannya kasar, tetapi ia hanya membetulkan cepat dan mudah, dan ia secara terang -terangan melanggar prinsip pembukaan dan penutupan. Sebaliknya, kaedah pertanyaan penyesuai () boleh dipadatkan semula untuk dipadankan dengan tandatangan kelas induk menulis semula. Tetapi dengan berbuat demikian, semua syarat lain yang dinyatakan oleh LSP juga harus dipenuhi. Singkatnya, ini bermakna kaedah penulisan semula harus dilakukan dengan berhati -hati dan hanya boleh dilakukan untuk alasan yang sangat kuat. Dalam banyak kes penggunaan, dengan mengandaikan bahawa antara muka tidak boleh digunakan, lebih baik untuk membuat subclass yang hanya meluas (bukannya mengatasi) fungsi kelas asasnya. Dalam kes penyesuai PDO NEO, pendekatan ini akan berfungsi dengan sempurna dan tidak akan memecahkan kod klien di mana -mana peringkat. Seperti yang saya katakan, terdapat penyelesaian yang lebih efisien - tetapi lebih radikal - yang mengambil kesempatan untuk melaksanakan antara muka. Walaupun penyesuai PDO terdahulu dicipta melalui warisan dan tidak dapat dinafikan melanggar ajaran LSP, kelemahan sebenarnya datang dari cara kelas Agen Mapper pada asalnya direka. Malah, ia bergantung kepada pelaksanaan penyesuai pangkalan data konkrit dari atas ke bawah, dan bukannya pada kontrak yang ditetapkan antara muka. Dan kuasa OO yang besar telah dikatakan sejak zaman purba, ini selalu menjadi perkara yang buruk. Jadi, bagaimanakah penyelesaian di atas dilaksanakan?

(selebihnya sama dengan teks input, dan boleh diselaraskan dan dipermudahkan seperti yang diperlukan)

Atas ialah kandungan terperinci Prinsip penggantian Liskov. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel terbaru oleh pengarang
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan