mata teras
Ramai orang boleh meragui korelasi antara warisan dan polimorfisme dalam reka bentuk berorientasikan objek? Mungkin sedikit, kebanyakan mereka mungkin disebabkan oleh kejahilan atau pemikiran sempit. Tetapi ada masalah kecil di sini yang tidak boleh diabaikan. Walaupun mudah untuk memahami logik warisan, perkara menjadi lebih sukar apabila membincangkan butiran polimorfisme. Istilah "polimorfisme" adalah menakutkan dengan sendirinya, dengan definisi akademiknya penuh dengan perspektif yang berbeza, yang menjadikannya lebih sukar untuk memahami apa yang sebenarnya di belakangnya. Konsep periferal seperti polimorfisme parameter dan polimorfisme ad hoc (biasanya dilaksanakan dengan kaedah override/overload) mempunyai aplikasi yang penting dalam beberapa bahasa pengaturcaraan, tetapi dalam reka bentuk, mereka boleh mengambil kontrak tertentu (bacaan) apabila abstrak) harus ditinggalkan tanpa memeriksa sama ada pelaksana adalah jenis yang diharapkan. Singkatnya, kebanyakan masa, apa-apa rujukan umum terhadap polimorfisme dalam pengaturcaraan berorientasikan objek secara tersirat dianggap sebagai keupayaan sistem-eksplisit yang digunakan untuk menentukan satu set kontrak atau antara muka yang atau antara muka diikuti oleh pelaksanaan yang berbeza. Polimorfisme "kanonik" ini sering dirujuk sebagai polimorfisme subtipe, kerana pelaksana antara muka dianggap sebagai subtipe mereka, tanpa mengira sama ada terdapat hierarki sebenar. Seperti yang dijangkakan, memahami sifat polimorfisme hanya separuh daripada proses pembelajaran; "Kod" (dalam banyak kes, ia adalah eufemisme yang murah untuk kod mainan). Dalam artikel ini, saya akan menunjukkan kepada anda bagaimana untuk memanfaatkan manfaat yang disediakan oleh polimorfisme dengan membangunkan komponen cache yang boleh dimasukkan. Fungsi teras kemudiannya boleh diperluaskan untuk memenuhi keperluan anda dengan membangunkan pemandu cache tambahan.
Tentukan antara muka dan pelaksanaan komponen
Menu pilihan untuk dipilih adalah tidak hadir ketika membina komponen cache yang boleh diperluaskan (jika anda ragu -ragu tentang ini, lihat saja apa yang ada di sebalik kerangka popular). Walau bagaimanapun, di sini, komponen yang saya berikan mempunyai keupayaan pintar untuk menukar pemandu cache yang berbeza pada masa runtime tanpa mengubah suai kod pelanggan. Jadi, bagaimana anda boleh melakukan ini tanpa banyak usaha semasa proses pembangunan? Nah, langkah pertama adalah ... Ya, tentukan kontrak cache terpencil yang akan diikuti oleh pelaksanaan yang berbeza kemudian, dengan itu memanfaatkan manfaat polimorfisme. Pada tahap yang paling asas, kontrak di atas adalah seperti berikut:
<?php namespace LibraryCache; interface CacheInterface { public function set($id, $data); public function get($id); public function delete($id); public function exists($id); }
CacheInterface
adalah kontrak rangka yang abstrak tingkah laku elemen cache biasa. Dengan antara muka, anda boleh dengan mudah membuat beberapa pelaksanaan cache tertentu yang mematuhi kontrak mereka. Oleh kerana saya ingin memastikan ia mudah dan mudah difahami, pemacu cache yang saya sediakan akan menjadi duo tanpa lemak: yang pertama menggunakan sistem fail sebagai backend asas untuk cache/mendapatkan data, sementara yang kedua menggunakan lanjutan APC Di belakang tabir. Berikut adalah pelaksanaan cache berasaskan fail:
<?php namespace LibraryCache; class FileCache implements CacheInterface { const DEFAULT_CACHE_DIRECTORY = 'Cache/'; private $cacheDir; public function __construct($cacheDir = self::DEFAULT_CACHE_DIRECTORY) { $this->setCacheDir($cacheDir); } public function setCacheDir($cacheDir) { if (!is_dir($cacheDir)) { if (!mkdir($cacheDir, 0644)) { throw InvalidArgumentException('The cache directory is invalid.'); } } $this->cacheDir = $cacheDir; return $this; } public function set($id, $data) { if (!file_put_contents($this->cacheDir . $id, serialize($data), LOCK_EX)) { throw new RuntimeException("Unable to cache the data with ID '$id'."); } return $this; } public function get($id) { if (!$data = unserialize(@file_get_contents($this->cacheDir . $id, false))) { throw new RuntimeException("Unable to get the data with ID '$id'."); } return $data; } public function delete($id) { if (!@unlink($this->cacheDir . $id)) { throw new RuntimeException("Unable to delete the data with ID '$id'."); } return $this; } public function exists($id) { return file_exists($this->cacheDir . $id); } }
. Walaupun keupayaan ini manis dan menawan, untuk bahagiannya, saya tidak akan menghargai ia memandangkan matlamat di sini adalah untuk membuat komponen cache yang boleh menukar backends pada masa runtime. Marilah kita berusaha untuk tujuan pengajaran dan membawa satu lagi pelaksanaan yang diselaraskan FileCache
ke kehidupan. Pelaksanaan berikut mematuhi kontrak antara muka, tetapi kali ini dengan menggunakan APC untuk memperluaskan kaedah bundling: CacheInterface
CacheInterface
Kelas
<?php namespace LibraryCache; class ApcCache implements CacheInterface { public function set($id, $data, $lifeTime = 0) { if (!apc_store($id, $data, (int) $lifeTime)) { throw new RuntimeException("Unable to cache the data with ID '$id'."); } } public function get($id) { if (!$data = apc_fetch($id)) { throw new RuntimeException("Unable to get the data with ID '$id'."); } return $data; } public function delete($id) { if (!apc_delete($id)) { throw new RuntimeException("Unable to delete the data with ID '$id'."); } } public function exists($id) { return apc_exists($id); } }
. Walau bagaimanapun, saya harus menekankan bahawa polimorfisme subtipe sebenar dicapai dengan melaksanakan kontrak yang ditakrifkan melalui pembinaan antara muka, yang merupakan pendekatan yang sangat biasa. Walau bagaimanapun, tiada apa yang boleh menghalang anda daripada menjadi kurang ortodoks dan mendapatkan hasil yang sama dengan menukar antara muka yang diisytiharkan sebagai satu set kaedah abstrak (terletak di kelas abstrak). Sekiranya anda merasa berisiko dan ingin pergi ke laluan itu, anda boleh membina semula kontrak dan pelaksanaan yang sepadan seperti berikut: ApcCache
<?php namespace LibraryCache; interface CacheInterface { public function set($id, $data); public function get($id); public function delete($id); public function exists($id); }
<?php namespace LibraryCache; class FileCache implements CacheInterface { const DEFAULT_CACHE_DIRECTORY = 'Cache/'; private $cacheDir; public function __construct($cacheDir = self::DEFAULT_CACHE_DIRECTORY) { $this->setCacheDir($cacheDir); } public function setCacheDir($cacheDir) { if (!is_dir($cacheDir)) { if (!mkdir($cacheDir, 0644)) { throw InvalidArgumentException('The cache directory is invalid.'); } } $this->cacheDir = $cacheDir; return $this; } public function set($id, $data) { if (!file_put_contents($this->cacheDir . $id, serialize($data), LOCK_EX)) { throw new RuntimeException("Unable to cache the data with ID '$id'."); } return $this; } public function get($id) { if (!$data = unserialize(@file_get_contents($this->cacheDir . $id, false))) { throw new RuntimeException("Unable to get the data with ID '$id'."); } return $data; } public function delete($id) { if (!@unlink($this->cacheDir . $id)) { throw new RuntimeException("Unable to delete the data with ID '$id'."); } return $this; } public function exists($id) { return file_exists($this->cacheDir . $id); } }
<?php namespace LibraryCache; class ApcCache implements CacheInterface { public function set($id, $data, $lifeTime = 0) { if (!apc_store($id, $data, (int) $lifeTime)) { throw new RuntimeException("Unable to cache the data with ID '$id'."); } } public function get($id) { if (!$data = apc_fetch($id)) { throw new RuntimeException("Unable to get the data with ID '$id'."); } return $data; } public function delete($id) { if (!apc_delete($id)) { throw new RuntimeException("Unable to delete the data with ID '$id'."); } } public function exists($id) { return apc_exists($id); } }
Dari atas ke bawah, ini memang pendekatan polimorfik, yang bertentangan dengan kaedah yang telah dibincangkan sebelumnya. Secara peribadi, ini hanya pernyataan peribadi saya, saya lebih suka menggunakan antara muka membina untuk menentukan kontrak dan menggunakan kelas abstrak hanya apabila merangkumi pelaksanaan boilerplate yang dikongsi oleh beberapa subtipe. Anda boleh memilih kaedah yang paling sesuai dengan keperluan anda. Pada ketika ini, saya dapat meletakkan tirai, menulis beberapa komen mewah yang mewah, membanggakan tentang kemahiran pengekodan kami yang mengagumkan, dan membual tentang fleksibiliti komponen cache kami, tetapi itu akan menjadi slouch kepada kami. Apabila terdapat kod klien yang boleh menggunakan pelbagai pelaksanaan, polimorfisme mempamerkan aspek yang paling menggoda tanpa memeriksa sama ada pelaksanaan ini adalah contoh beberapa jenis, selagi mereka memenuhi kontrak yang diharapkan. Oleh itu, mari kita mendedahkan aspek dengan menghubungkan komponen cache ke kelas pandangan pelanggan asas, yang akan membolehkan kita melakukan beberapa caching HTML yang kemas dengan mudah.
Letakkan pemacu cache ke dalam penggunaan
output HTML caching melalui modul cache contoh kami sangat mudah dan saya akan menyimpan penjelasan yang panjang pada masa lain. Keseluruhan proses cache dapat dipermudahkan ke dalam kelas pandangan yang mudah, sama seperti yang berikut:
<?php namespace LibraryCache; abstract class AbstractCache { abstract public function set($id, $data); abstract public function get($id); abstract public function delete($id); abstract public function exists($id); }
<?php namespace LibraryCache; class FileCache extends AbstractCache { // the same implementation goes here }
dan CacheInterface
kaedah. Memandangkan tanggungjawab kaedah terakhir adalah untuk menyingkirkan templat pandangan selepas ia ditolak ke penampan output, lebih baik untuk memanfaatkan keupayaan dan cache keseluruhan dokumen HTML. Anggapkan bahawa templat lalai pandangan mempunyai struktur berikut: render()
<?php namespace LibraryCache; class ApcCache extends AbstractCache { // the same implementation goes here }
ke pandangan: ApcCache
<?php namespace LibraryView; interface ViewInterface { public function setTemplate($template); public function __set($field, $value); public function __get($field); public function render(); }
<?php namespace LibraryView; use LibraryCacheCacheInterface; class View implements ViewInterface { const DEFAULT_TEMPLATE = 'default'; private $template; private $fields = array(); private $cache; public function __construct(CacheInterface $cache, $template = self::DEFAULT_TEMPLATE) { $this->cache = $cache; $this->setTemplate($template); } public function setTemplate($template) { $template = $template . '.php'; if (!is_file($template) || !is_readable($template)) { throw new InvalidArgumentException( "The template '$template' is invalid."); } $this->template = $template; return $this; } public function __set($name, $value) { $this->fields[$name] = $value; return $this; } public function __get($name) { if (!isset($this->fields[$name])) { throw new InvalidArgumentException( "Unable to get the field '$field'."); } return $this->fields[$name]; } public function render() { try { if (!$this->cache->exists($this->template)) { extract($this->fields); ob_start(); include $this->template; $this->cache->set($this->template, ob_get_clean()); } return $this->cache->get($this->template); } catch (RuntimeException $e) { throw new Exception($e->getMessage()); } } }
Kesimpulan
Polymorphism memang salah satu perkara yang baik dalam hidup, dan apabila anda memahaminya, ia membuat anda tertanya -tanya bagaimana anda boleh lakukan tanpa kesnya berterusan selama sekian lama. Sistem polimorfik sememangnya lebih ortogonal, lebih mudah untuk skala, dan kurang terdedah kepada melanggar paradigma teras seperti prinsip terbuka/tertutup dan prinsip pengaturcaraan berorientasikan antara muka yang bijak. Walaupun agak primitif, modul cache kami adalah contoh yang menonjol dari kelebihan ini. Sekiranya anda tidak memaklumkan permohonan anda untuk memanfaatkan manfaat polimorfisme, anda akan lebih cepat tergesa -gesa kerana anda terlepas jackpot! gambar dari Fotolia
Soalan Lazim Mengenai Polymorphisms Subtipe (FAQ)
Bagaimana polimorfisme subtipe berfungsi di Java?
Bolehkah anda memberikan contoh polimorfisme subtipe?
Apakah kepentingan polimorfisme subtipe dalam pengaturcaraan?
Apakah hubungan antara polimorfisme subtipe dan prinsip penggantian Liskov?
Tidak, tidak semua bahasa pengaturcaraan menyokong polimorfisme subtipe. Ini terutamanya ciri bahasa pengaturcaraan berorientasikan objek yang ditaip secara statik seperti Java, C, dan C#. Bahasa yang ditaip secara dinamik seperti Python dan JavaScript mempunyai bentuk polimorfisme yang berbeza, yang dipanggil jenis itik.
Polimorfisme statik, juga dikenali sebagai polimorfisme kompilasi masa, dicapai melalui kaedah yang berlebihan. Keputusan mengenai kaedah yang hendak dipanggil dibuat pada masa penyusunan. Sebaliknya, polimorfisme dinamik, yang juga dikenali sebagai polimorfisme runtime, dilaksanakan melalui penulisan semula kaedah. Keputusan mengenai kaedah yang hendak dipanggil dibuat pada masa runtime. Polimorfisme subtipe adalah polimorfisme yang dinamik.
Upconversion adalah proses merawat objek kelas yang diperolehi sebagai objek kelas asas. Ia adalah aspek utama polimorfisme subtipe. Apabila anda menaikkan objek kelas yang diperoleh, anda boleh memanggil sebarang kaedah yang ditakrifkan dalam kelas asas. Walau bagaimanapun, jika kaedah itu ditulis semula dalam kelas yang diperolehi, versi penulisan semula akan dipanggil.
penukaran turun adalah bertentangan dengan penukaran UP. Ia adalah proses menukarkan objek superclass ke dalam subkelas. Apabila anda perlu mengakses kaedah yang hanya wujud dalam subkelas, anda boleh menggunakan penukaran bawah. Walau bagaimanapun, downconversion boleh berbahaya kerana ia boleh menyebabkan ClassCastException jika objek yang ditukar sebenarnya tidak mempunyai jenis yang anda bertukar.
polimorfisme subtipe membolehkan kita menulis lebih banyak kod umum dan boleh diguna semula. Dengan menggunakan rujukan superclass untuk berinteraksi dengan objek subclass, kita boleh menulis kod untuk pelbagai objek selagi mereka semua tergolong dalam subclass superclass yang sama. Ini bermakna kita boleh menambah subkelas baru tanpa mengubah kod yang menggunakan superclass, yang menjadikan kod kita lebih fleksibel dan lebih mudah untuk dikekalkan.
Atas ialah kandungan terperinci Polimorfisme Subtipe - Pelaksanaan Tukar pada Runtime. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!