Rumah >pembangunan bahagian belakang >tutorial php >Prinsip penyongsangan ketergantungan
mata teras
Pada pandangan pertama, pernyataan di atas kelihatan jelas. Memandangkan tiada siapa yang tidak bersetuju bahawa sistem yang dibina atas pergantungan yang kuat terhadap pelaksanaan konkrit adalah pertanda yang tidak baik dari reka bentuk yang buruk, adalah benar -benar munasabah untuk menukar beberapa abstraksi. Oleh itu, ini akan membawa kita kembali ke titik permulaan, memikirkan bahawa tumpuan utama DIP adalah mengenai pengaturcaraan berorientasikan antara muka. Malah, decoupling antara muka dari pelaksanaan hanya satu kaedah yang siap ketika memenuhi keperluan prinsip ini. Bahagian yang hilang adalah untuk melaksanakan proses penyongsangan sebenar. Sudah tentu, persoalan timbul: Apakah pembalikan? Secara tradisinya, sistem sentiasa direka untuk membuat komponen peringkat tinggi (sama ada mereka kelas atau rutin proses) bergantung kepada komponen peringkat rendah (butiran). Sebagai contoh, modul pembalakan mungkin mempunyai kebergantungan yang kuat pada satu siri pembalak tertentu (sebenarnya merakam maklumat ke dalam sistem). Oleh itu, apabila protokol logger diubah suai, skim ini akan bising melewati kesan sampingan ke lapisan atas, walaupun protokol telah dicabut. Walau bagaimanapun, pelaksanaan DIP membantu beberapa tahap untuk mengurangkan riak -riak ini dengan mempunyai modul pembalakan mempunyai protokol, sehingga membalikkan aliran ketergantungan keseluruhan. Selepas penyongsangan, pembalak harus setia mematuhi protokol, jadi jika terdapat perubahan pada masa akan datang, mereka harus berubah dengan sewajarnya dan menyesuaikan diri dengan turun naik protokol. Singkatnya, ini menunjukkan bahawa Dip adalah sedikit lebih rumit di belakang tabir daripada bergantung semata -mata pada antara muka standard - melaksanakan decoupling. Ya, ia membincangkan membuat kedua-dua modul peringkat tinggi dan rendah bergantung kepada abstraksi, tetapi pada masa yang sama modul peringkat tinggi mesti mempunyai abstraksi ini-butiran halus tetapi relevan yang tidak dapat diabaikan dengan mudah. Seperti yang anda harapkan, satu cara yang mungkin membantu anda memahami apa yang dipeluk sebenarnya adalah melalui beberapa contoh kod praktikal. Oleh itu, dalam artikel ini, saya akan menyediakan beberapa contoh supaya anda dapat belajar bagaimana untuk memanfaatkan prinsip pepejal ini apabila membangunkan aplikasi PHP.
Membangunkan modul penyimpanan mudah (yang hilang "I" dalam dip)
Ramai pemaju, terutama mereka yang membenci PHP berorientasikan objek, cenderung melihat dip dan prinsip-prinsip pepejal lain sebagai dogma tegar, bertentangan dengan pragmatisme yang wujud dalam bahasa. Saya dapat memahami idea ini kerana sukar untuk mencari contoh PHP praktikal di alam liar yang mempamerkan manfaat sebenar prinsip. Saya tidak cuba untuk menceritakan diri saya sebagai pengaturcara yang tercerahkan (saman itu tidak sesuai dengan baik), tetapi ia masih berguna untuk bekerja keras untuk matlamat yang baik dan menunjukkan dari sudut pandang praktikal bagaimana untuk melaksanakan Dip dalam kes penggunaan sebenar. Pertama, pertimbangkan pelaksanaan modul penyimpanan fail mudah. Modul ini bertanggungjawab untuk membaca dan menulis data dari fail sasaran yang ditentukan. Pada tahap yang sangat mudah, modul dalam soalan boleh ditulis seperti ini:
<code class="language-php"><?php namespace LibraryEncoderStrategy; class Serializer implements Serializable { protected $unserializeCallback; public function __construct($unserializeCallback = false) { $this->unserializeCallback = (boolean) $unserializeCallback; } public function getUnserializeCallback() { return $this->unserializeCallback; } public function serialize($data) { if (is_resource($data)) { throw new InvalidArgumentException( "PHP resources are not serializable."); } if (($data = serialize($data)) === false) { throw new RuntimeException( "Unable to serialize the supplied data."); } return $data; } public function unserialize($data) { if (!is_string($data) || empty($data)) { throw new InvalidArgumentException( "The data to be decoded must be a non-empty string."); } if ($this->unserializeCallback) { $callback = ini_get("unserialize_callback_func"); if (!function_exists($callback)) { throw new BadFunctionCallException( "The php.ini unserialize callback function is invalid."); } } if (($data = @unserialize($data)) === false) { throw new RuntimeException( "Unable to unserialize the supplied data."); } return $data; } }</code>
<code class="language-php"><?php namespace LibraryFile; class FileStorage { const DEFAULT_STORAGE_FILE = "default.dat"; protected $serializer; protected $file; public function __construct(Serializable $serializer, $file = self::DEFAULT_STORAGE_FILE) { $this->serializer = $serializer; $this->setFile($file); } public function getSerializer() { return $this->serializer; } public function setFile($file) { if (!is_file($file) || !is_readable($file)) { throw new InvalidArgumentException( "The supplied file is not readable or writable."); } $this->file = $file; return $this; } public function getFile() { return $this->file; } public function resetFile() { $this->file = self::DEFAULT_STORAGE_FILE; return $this; } public function write($data) { try { return file_put_contents($this->file, $this->serializer->serialize($data)); } catch (Exception $e) { throw new Exception($e->getMessage()); } } public function read() { try { return $this->serializer->unserialize( @file_get_contents($this->file)); } catch (Exception $e) { throw new Exception($e->getMessage()); } } }</code>
Modul ini adalah struktur yang agak mudah yang terdiri daripada hanya beberapa komponen asas. Kelas pertama membaca dan menulis data dari sistem fail, dan kelas kedua adalah serializer PHP yang mudah untuk menghasilkan perwakilan data yang boleh disimpan secara dalaman. Komponen sampel ini melaksanakan perniagaan mereka dengan baik secara berasingan dan boleh dihubungkan bersama seperti ini untuk berfungsi serentak:
<code class="language-php"><?php use LibraryLoaderAutoloader, LibraryEncoderStrategySerializer, LibraryFileFileStorage; require_once __DIR__ . "/Library/Loader/Autoloader.php"; $autoloader = new Autoloader; $autoloader->register(); $fileStorage = new FileStorage(new Serializer); $fileStorage->write(new stdClass()); print_r($fileStorage->read()); $fileStorage->write(array("This", "is", "a", "sample", "array")); print_r($fileStorage->read()); $fileStorage->write("This is a sample string."); echo $fileStorage->read();</code>
Pada pandangan pertama, modul mempamerkan tingkah laku yang cukup baik memandangkan fungsi modul membolehkan penyimpanan dan pengambilalihan mudah dari pelbagai data dari sistem fail. Di samping itu, kelas Filestorage menyuntik antara muka bersiri ke dalam pembina, dengan itu bergantung pada fleksibiliti yang disediakan oleh abstraksi dan bukannya pelaksanaan konkrit yang tegar. Dengan kelebihan ini, apakah masalah dengan modul ini? Sering kali, kesan pertama yang cetek boleh menjadi rumit dan samar -samar. Sekiranya anda melihat dengan teliti, bukan sahaja filestorage sebenarnya bergantung pada serializer, tetapi kerana ketergantungan yang ketat ini, menyimpan dan mengekstrak data dari fail sasaran adalah terhad kepada mekanisme siriisasi asli menggunakan PHP. Apa yang berlaku jika data mesti diluluskan sebagai XML atau JSON ke perkhidmatan luaran? Modul yang direka dengan baik tidak lagi boleh diguna semula. Sedih tapi nyata! Keadaan ini menimbulkan beberapa soalan yang menarik. Pertama dan terpenting, filestorage masih menunjukkan kebergantungan yang kuat terhadap serializers peringkat rendah walaupun protokol yang menjadikannya saling beroperasi telah diasingkan dari pelaksanaannya. Kedua, tahap universiti yang didedahkan oleh protokol dalam masalah ini sangat terhad, dan ia terhad untuk menukar satu serializer kepada yang lain. Dalam kes ini, bergantung kepada abstraksi adalah persepsi ilusi, dan proses penyongsangan sebenar yang digalakkan oleh DIP tidak pernah dilaksanakan. Sesetengah bahagian modul fail boleh refactored untuk mematuhi keperluan Dip dengan setia. Dengan berbuat demikian, kelas FileStorage akan mendapat pemilikan protokol yang digunakan untuk menyimpan dan mengekstrak data fail, dengan itu menyingkirkan kebergantungan pada serializers peringkat rendah dan membolehkan anda beralih antara pelbagai dasar penyimpanan pada runtime. Melakukannya, anda sebenarnya akan mendapat banyak fleksibiliti secara percuma. Oleh itu, mari kita teruskan dan lihat bagaimana untuk menukar modul penyimpanan fail ke struktur yang benar-benar dipindahkan.
pemilikan protokol dan decoupling antara muka dan pelaksanaan (mengekalkan penggunaan penuh DIP)
Walaupun tidak banyak pilihan, masih ada cara untuk membalikkan pemilikan protokol dengan berkesan antara kelas Filestorage dan kolaborator peringkat rendahnya sambil mengekalkan abstraksi protokol. Walau bagaimanapun, terdapat kaedah yang sangat intuitif kerana ia bergantung pada enkapsulasi semulajadi ruang nama PHP keluar dari kotak. Untuk menterjemahkan konsep yang agak sukar difahami ini ke dalam kod konkrit, perubahan pertama yang harus dibuat kepada modul adalah untuk menentukan protokol longgar untuk menyimpan dan mengambil data fail supaya ia dapat dimanipulasi dengan mudah dalam format selain daripada php serialization. Antara muka yang diselaraskan, terpencil seperti yang ditunjukkan di bawah boleh melakukan pekerjaan dengan anggun dan hanya:
<code class="language-php"><?php namespace LibraryEncoderStrategy; class Serializer implements Serializable { protected $unserializeCallback; public function __construct($unserializeCallback = false) { $this->unserializeCallback = (boolean) $unserializeCallback; } public function getUnserializeCallback() { return $this->unserializeCallback; } public function serialize($data) { if (is_resource($data)) { throw new InvalidArgumentException( "PHP resources are not serializable."); } if (($data = serialize($data)) === false) { throw new RuntimeException( "Unable to serialize the supplied data."); } return $data; } public function unserialize($data) { if (!is_string($data) || empty($data)) { throw new InvalidArgumentException( "The data to be decoded must be a non-empty string."); } if ($this->unserializeCallback) { $callback = ini_get("unserialize_callback_func"); if (!function_exists($callback)) { throw new BadFunctionCallException( "The php.ini unserialize callback function is invalid."); } } if (($data = @unserialize($data)) === false) { throw new RuntimeException( "Unable to unserialize the supplied data."); } return $data; } }</code>
Kewujudan EncoderInterface nampaknya tidak mempunyai kesan yang mendalam terhadap reka bentuk keseluruhan modul fail, tetapi ia lebih banyak daripada yang dijanjikan secara dangkal. Penambahbaikan pertama adalah definisi protokol yang sangat umum untuk mengekod dan menyahkod data. Peningkatan kedua adalah sama pentingnya dengan yang pertama, iaitu pemilikan protokol kini milik kelas Filestorage kerana antara muka wujud di ruang nama kelas. Singkatnya, kami berjaya membuat pengekod/penyahkod rendah peringkat rendah yang masih belum ditentukan bergantung kepada filestorage peringkat tinggi hanya dengan menulis antara muka dengan ruang nama yang betul. Singkatnya, ini adalah proses pembalikan sebenar yang mencelupkan penyokong di belakang kerudung akademiknya. Sudah tentu, jika kelas Filestorage tidak diubahsuai untuk menjadi pelaksana yang menyuntik antara muka terdahulu, maka penyongsangan akan menjadi percubaan separuh jalan yang kekok, jadi inilah versi refactored:
<code class="language-php"><?php namespace LibraryFile; class FileStorage { const DEFAULT_STORAGE_FILE = "default.dat"; protected $serializer; protected $file; public function __construct(Serializable $serializer, $file = self::DEFAULT_STORAGE_FILE) { $this->serializer = $serializer; $this->setFile($file); } public function getSerializer() { return $this->serializer; } public function setFile($file) { if (!is_file($file) || !is_readable($file)) { throw new InvalidArgumentException( "The supplied file is not readable or writable."); } $this->file = $file; return $this; } public function getFile() { return $this->file; } public function resetFile() { $this->file = self::DEFAULT_STORAGE_FILE; return $this; } public function write($data) { try { return file_put_contents($this->file, $this->serializer->serialize($data)); } catch (Exception $e) { throw new Exception($e->getMessage()); } } public function read() { try { return $this->serializer->unserialize( @file_get_contents($this->file)); } catch (Exception $e) { throw new Exception($e->getMessage()); } } }</code>
Filestorage kini secara eksplisit mengisytiharkan pemilikan protokol pengekodan/penyahkodan dalam pembina, dan satu-satunya perkara yang tersisa adalah untuk membuat satu set spesifik pengekod/decoder peringkat rendah yang membolehkan anda mengendalikan data fail dalam pelbagai format. Yang pertama dari komponen ini hanyalah pelaksanaan refactoring PHP Serializer yang ditulis sebelum ini:
<code class="language-php"><?php use LibraryLoaderAutoloader, LibraryEncoderStrategySerializer, LibraryFileFileStorage; require_once __DIR__ . "/Library/Loader/Autoloader.php"; $autoloader = new Autoloader; $autoloader->register(); $fileStorage = new FileStorage(new Serializer); $fileStorage->write(new stdClass()); print_r($fileStorage->read()); $fileStorage->write(array("This", "is", "a", "sample", "array")); print_r($fileStorage->read()); $fileStorage->write("This is a sample string."); echo $fileStorage->read();</code>
Analisis Logik di belakang Serializer pasti berlebihan. Walau bagaimanapun, ia patut menunjukkan bahawa ia kini tidak hanya bergantung pada pengekodan/pengekodan pengekodan yang lebih longgar, tetapi pemilikan abstraksi secara eksplisit didedahkan di peringkat ruang nama. Sekali lagi, kita boleh melangkah lebih jauh dan mula menulis lebih banyak encoder untuk menyerlahkan manfaat DIP. Setelah mengatakan itu, inilah satu lagi komponen peringkat rendah tambahan yang akan ditulis:
<code class="language-php"><?php namespace LibraryFile; interface EncoderInterface { public function encode($data); public function decode($data); }</code>
Seperti yang dijangkakan, logik yang mendasari di belakang pengekod tambahan sering sama dengan Serializer PHP yang pertama, kecuali untuk penambahbaikan dan varian yang ketara. Di samping itu, komponen-komponen ini mematuhi keperluan yang dikenakan Dip dan oleh itu mematuhi protokol pengekodan/penyahkodan yang ditakrifkan dalam ruang nama filestorage. Oleh kerana kedua-dua komponen peringkat atas dan bawah dalam modul fail bergantung kepada abstraksi dan pengekod mempunyai pergantungan yang jelas pada kelas penyimpanan fail, kami dapat dengan selamat mendakwa bahawa modul itu berkelakuan sejajar dengan spesifikasi DIP. Di samping itu, contoh berikut menunjukkan cara menggabungkan komponen ini:
<code class="language-php"><?php namespace LibraryFile; class FileStorage { const DEFAULT_STORAGE_FILE = "default.dat"; protected $encoder; protected $file; public function __construct(EncoderInterface $encoder, $file = self::DEFAULT_STORAGE_FILE) { $this->encoder = $encoder; $this->setFile($file); } public function getEncoder() { return $this->encoder; } public function setFile($file) { if (!is_file($file) || !is_readable($file)) { throw new InvalidArgumentException( "The supplied file is not readable or writable."); } $this->file = $file; return $this; } public function getFile() { return $this->file; } public function resetFile() { $this->file = self::DEFAULT_STORAGE_FILE; return $this; } public function write($data) { try { return file_put_contents($this->file, $this->encoder->encode($data)); } catch (Exception $e) { throw new Exception($e->getMessage()); } } public function read() { try { return $this->encoder->decode( @file_get_contents($this->file)); } catch (Exception $e) { throw new Exception($e->getMessage()); } } }</code>
Selain dari beberapa kehalusan mudah yang modul mendedahkan kepada kod klien, sangat berguna untuk menjelaskan perkara-perkara utama dan menunjukkan dengan cara yang agak mengajar mengapa predikat Dip sebenarnya lebih luas daripada paradigma pengaturcaraan berorientasikan "berorientasikan antara muka" lama. Ia menerangkan dan secara jelas menentukan penyongsangan kebergantungan dan oleh itu harus dilaksanakan melalui mekanisme yang berbeza. Ruang nama PHP adalah cara yang baik untuk mencapai ini tanpa beban yang terlalu banyak, walaupun kaedah tradisional seperti mendefinisikan susun atur aplikasi yang sangat berstruktur, sangat ekspresif dapat menghasilkan hasil yang sama.
Kesimpulan
Seringkali, pendapat berdasarkan kepakaran subjektif sering berat sebelah, dan tentu saja, pandangan yang saya nyatakan pada permulaan artikel ini tidak terkecuali. Walau bagaimanapun, terdapat sedikit kecenderungan untuk mengabaikan prinsip penyongsangan ketergantungan untuk rakan sejawatannya yang lebih kompleks, kerana ia mudah disalahpahami sebagai sinonim untuk abstraksi ketergantungan. Selain itu, sesetengah pengaturcara cenderung bertindak balas secara intuitif dan memikirkan istilah "penyongsangan" sebagai ungkapan singkatan yang mengawal penyongsangan, dan sementara kedua -duanya berkaitan dengan satu sama lain, ini akhirnya merupakan konsep palsu. Sekarang bahawa anda tahu makna sebenar Dip, pastikan anda mengambil kesempatan daripada semua manfaat yang dibawa, yang pasti akan membuat permohonan anda kurang terdedah kepada masalah kelemahan dan kekakuan yang mungkin timbul dari masa ke masa. Gambar dari Kentoh/Shutterstock
Soalan Lazim mengenai Prinsip Inversion Reliance
Prinsip Inversi Ketergantungan (DIP) adalah aspek utama prinsip pepejal dalam pengaturcaraan berorientasikan objek. Tujuan utamanya adalah untuk meremehkan modul perisian. Ini bermakna bahawa modul peringkat tinggi yang menyediakan logik kompleks dipisahkan dari modul peringkat rendah yang menyediakan operasi asas. Dengan berbuat demikian, perubahan kepada modul peringkat rendah akan memberi impak minimum pada modul peringkat tinggi, menjadikan keseluruhan sistem lebih mudah untuk mengurus dan mengekalkan.
Pengaturcaraan programatik tradisional biasanya melibatkan modul peringkat tinggi yang bergantung pada modul peringkat rendah. Ini boleh membawa kepada sistem tegar di mana perubahan kepada satu modul boleh memberi kesan yang signifikan terhadap modul lain. Dip, sebaliknya, membalikkan pergantungan ini. Kedua-dua modul peringkat tinggi dan rendah bergantung kepada abstraksi, yang menggalakkan fleksibiliti dan menjadikan sistem lebih mudah disesuaikan dengan perubahan.
Sudah tentu, mari kita pertimbangkan contoh program mudah yang membaca data dari fail dan memprosesnya. Dalam kaedah tradisional, modul pemprosesan boleh bergantung secara langsung pada modul bacaan fail. Walau bagaimanapun, dengan DIP, kedua -dua modul akan bergantung pada abstraksi, seperti antara muka "DataReader". Ini bermakna modul pemprosesan tidak terikat secara langsung ke modul bacaan fail, dan kita boleh dengan mudah beralih ke sumber data yang berbeza (seperti pangkalan data atau perkhidmatan web) tanpa mengubah modul pemprosesan.
Dip boleh membawa beberapa faedah ke kod anda. Ia menggalakkan decoupling, yang menjadikan sistem anda lebih fleksibel dan lebih mudah untuk diubah suai. Ia juga meningkatkan kesesuaian kod, kerana kebergantungan dapat dengan mudah dihina atau ditangkap. Selain itu, ia menggalakkan amalan reka bentuk yang baik seperti pengaturcaraan berorientasikan antara muka dan bukannya pengaturcaraan berorientasikan pelaksanaan.
Walaupun DIP mempunyai banyak kelebihan, ia juga boleh memperkenalkan kerumitan, terutamanya dalam sistem besar di mana bilangan abstraksi boleh menjadi sukar untuk dikendalikan. Ia juga boleh membawa kepada lebih banyak penulisan kod, kerana anda perlu menentukan antara muka dan mungkin membuat kelas lain untuk melaksanakannya. Walau bagaimanapun, cabaran -cabaran ini dapat dikurangkan oleh reka bentuk yang baik dan amalan seni bina.
Dip adalah prinsip terakhir dalam singkatan pepejal, tetapi ia berkait rapat dengan prinsip -prinsip lain. Sebagai contoh, kedua -dua Prinsip Tanggungjawab Tunggal (SRP) dan Prinsip Terbuka dan Tutup (OCP) mempromosikan decoupling, yang merupakan aspek utama DIP. Kedua -dua Prinsip Penggantian Richter (LSP) dan Prinsip Pengasingan Antara Muka (ISP) berurusan dengan abstraksi, yang berada di tengah -tengah DIP.
benar -benar. Walaupun DIP biasanya dibincangkan dalam konteks Java dan bahasa berorientasikan objek lain, prinsip itu sendiri adalah bahasa yang bebas. Anda boleh memohon dips, seperti antara muka atau kelas abstrak dalam mana -mana bahasa yang menyokong abstraksi.
Titik permulaan yang baik adalah untuk mencari kawasan dalam kod anda di mana modul peringkat tinggi bergantung secara langsung pada modul peringkat rendah. Pertimbangkan sama ada anda boleh memperkenalkan abstraksi antara modul -modul ini untuk meremehkannya. Ingatlah bahawa matlamatnya bukan untuk menghapuskan semua kebergantungan langsung, tetapi untuk memastikan bahawa kebergantungan bertujuan untuk abstraksi, bukan pelaksanaan konkrit.
Dip digunakan terutamanya untuk memperbaiki struktur dan mengekalkan kod, dan bukannya prestasinya. Walau bagaimanapun, dengan menjadikan kod anda lebih modular dan lebih mudah difahami, ia dapat membantu anda mengenal pasti dan menyelesaikan kesesakan prestasi dengan lebih cekap.
Walaupun manfaat DIP sering lebih jelas dalam sistem kompleks yang besar, ia juga boleh berguna dalam projek yang lebih kecil. Malah dalam pangkalan kod kecil, modul decoupling boleh menjadikan kod anda lebih mudah difahami, menguji, dan mengubah suai.
Atas ialah kandungan terperinci Prinsip penyongsangan ketergantungan. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!