Saya mengikuti pertanyaan yang sangat masuk akal ini tentang kemungkinan pelanggaran prinsip Pergantian Liskov. Saya tahu apa prinsip Substitusi Liskov, tetapi yang masih belum jelas dalam benak saya adalah apa yang salah jika saya sebagai pengembang tidak memikirkan prinsip tersebut saat menulis kode berorientasi objek.
27
Jawaban:
Saya pikir itu dinyatakan dengan sangat baik dalam pertanyaan itu yang merupakan salah satu alasan yang terpilih sangat tinggi.
Bayangkan jika Anda mau:
Dalam metode ini, kadang-kadang panggilan .Close () akan meledak, jadi sekarang berdasarkan implementasi konkret dari tipe turunan Anda harus mengubah cara metode ini berperilaku dari bagaimana metode ini akan ditulis jika Tugas tidak memiliki subtipe yang bisa menjadi diserahkan ke metode ini.
Karena pelanggaran substitusi liskov, kode yang menggunakan tipe Anda harus memiliki pengetahuan eksplisit tentang cara kerja internal tipe turunan untuk memperlakukannya secara berbeda. Kode ini sangat erat berpasangan dan secara umum membuat implementasi lebih sulit untuk digunakan secara konsisten.
sumber
Jika Anda tidak memenuhi kontrak yang telah ditentukan dalam kelas dasar, semuanya dapat gagal saat Anda mendapatkan hasil yang tidak aktif.
LSP di negara bagian wikipedia
Jika ada yang tidak tahan, penelepon mungkin mendapatkan hasil yang tidak diharapkannya.
sumber
Pertimbangkan kasus klasik dari catatan pertanyaan wawancara: Anda telah memperoleh Circle dari Ellipse. Mengapa? Karena sebuah lingkaran berbentuk elips, tentu saja!
Kecuali ... ellipse memiliki dua fungsi:
Jelas, ini harus didefinisikan ulang untuk Lingkaran, karena Lingkaran memiliki jari-jari yang seragam. Anda memiliki dua kemungkinan:
Sebagian besar bahasa OO tidak mendukung yang kedua, dan untuk alasan yang baik: akan mengejutkan untuk menemukan bahwa Lingkaran Anda tidak lagi menjadi Lingkaran. Jadi opsi pertama adalah yang terbaik. Tetapi pertimbangkan fungsi berikut:
Bayangkan beberapa panggilan fungsi e.set_alpha_radius. Tetapi karena e benar - benar sebuah Circle, secara mengejutkan jari-jari beta-nya juga diatur.
Dan di sinilah letak prinsip substitusi: sebuah subclass harus dapat diganti dengan superclass. Kalau tidak, hal-hal mengejutkan terjadi.
sumber
Dalam kata-kata awam:
Kode Anda akan memiliki banyak sekali KASUS / saklar klausa di seluruh.
Setiap salah satu dari klausa KASUS / sakelar ini akan membutuhkan case baru yang ditambahkan dari waktu ke waktu, artinya basis kode tidak scalable dan dapat dikelola sebagaimana mestinya.
LSP memungkinkan kode bekerja lebih seperti perangkat keras:
Anda tidak perlu memodifikasi iPod Anda karena Anda membeli sepasang speaker eksternal baru, karena speaker eksternal lama dan baru menghargai antarmuka yang sama, mereka dapat dipertukarkan tanpa iPod kehilangan fungsionalitas yang diinginkan.
sumber
typeof(someObject)
untuk memutuskan apa yang "diizinkan untuk Anda lakukan", maka tentu saja, tetapi itu adalah anti-pola lain sama sekali.untuk memberikan contoh kehidupan nyata dengan UndoManager java
itu mewarisi dari
AbstractUndoableEdit
yang kontraknya menentukan bahwa ia memiliki 2 negara (dibatalkan dan redone) dan dapat pergi di antara mereka dengan satu panggilan keundo()
danredo()
namun UndoManager memiliki lebih banyak status dan bertindak seperti pembatalan pembatalan (setiap panggilan untuk
undo
membatalkan beberapa tetapi tidak semua suntingan, melemahkan postcondition)ini mengarah ke situasi hipotetis di mana Anda menambahkan UndoManager ke CompoundEdit sebelum menelepon
end()
kemudian memanggil undo pada CompoundEdit yang akan mengarahkannya untuk memanggilundo()
setiap pengeditan setelah membiarkan hasil edit Anda sebagian dibatalkanSaya menggulung sendiri
UndoManager
untuk menghindari itu (saya mungkin harus mengubah nama menjadiUndoBuffer
meskipun)sumber
Contoh: Anda bekerja dengan kerangka UI, dan Anda membuat kontrol UI kustom Anda sendiri dengan mensubkelas
Control
kelas dasar. KelasControl
dasar mendefinisikan metodegetSubControls()
yang harus mengembalikan koleksi kontrol bersarang (jika ada). Tetapi Anda mengganti metode untuk benar-benar mengembalikan daftar tanggal lahir presiden Amerika Serikat.Jadi apa yang salah dengan ini? Jelas bahwa rendering kontrol akan gagal, karena Anda tidak mengembalikan daftar kontrol seperti yang diharapkan. Kemungkinan besar UI akan macet. Anda melanggar kontrak yang harus dipatuhi subclass Kontrol.
sumber
Anda juga dapat melihatnya dari sudut pandang pemodelan. Ketika Anda mengatakan bahwa instance kelas
A
juga merupakan instance kelasB
Anda menyiratkan bahwa "perilaku yang dapat diamati dari instance kelasA
juga dapat diklasifikasikan sebagai perilaku yang dapat diamati dari instance kelasB
" (Ini hanya mungkin jika kelasB
kurang spesifik daripada kelasA
.)Jadi, melanggar LSP berarti ada beberapa kontradiksi dalam desain Anda: Anda mendefinisikan beberapa kategori untuk objek Anda dan kemudian Anda tidak menghargai mereka dalam implementasi Anda, sesuatu pasti salah.
Seperti membuat kotak dengan tag: "Kotak ini hanya berisi bola biru", dan kemudian melemparkan bola merah ke dalamnya. Apa gunanya tag seperti itu jika menunjukkan informasi yang salah?
sumber
Saya mewarisi basis kode baru-baru ini yang memiliki beberapa pelanggar Liskov besar di dalamnya. Di kelas-kelas penting. Ini telah menyebabkan saya sangat sakit. Biarkan saya jelaskan alasannya.
Saya punya
Class A
, yang berasal dariClass B
.Class A
danClass B
berbagi banyak properti yangClass A
menimpa dengan implementasinya sendiri. Pengaturan atau mendapatkanClass A
properti memiliki efek berbeda untuk pengaturan atau mendapatkan properti yang sama persis dariClass B
.Mengesampingkan fakta bahwa ini adalah cara yang benar-benar mengerikan untuk melakukan terjemahan dalam .NET, ada sejumlah masalah lain dengan kode ini.
Dalam hal
Name
ini digunakan sebagai indeks dan variabel kontrol aliran di sejumlah tempat. Kelas-kelas di atas berserakan di seluruh basis kode dalam bentuk mentah dan turunannya. Melanggar prinsip substitusi Liskov dalam hal ini berarti bahwa saya perlu mengetahui konteks setiap panggilan tunggal ke masing-masing fungsi yang mengambil kelas dasar.Kode menggunakan objek keduanya
Class A
danClass B
, jadi saya tidak bisa membuatClass A
abstrak untuk memaksa orang untuk menggunakanClass B
.Ada beberapa fungsi utilitas yang sangat berguna yang beroperasi
Class A
dan fungsi utilitas lain yang sangat berguna yang beroperasiClass B
. Idealnya saya ingin dapat menggunakan fungsi utilitas yang dapat beroperasi padaClass A
padaClass B
. Banyak fungsi yang mengambil aClass B
bisa dengan mudah diambilClass A
jika bukan karena pelanggaran LSP.Hal terburuk tentang ini adalah bahwa kasus khusus ini sangat sulit untuk diperbaiki karena seluruh aplikasi bergantung pada dua kelas ini, beroperasi pada kedua kelas setiap saat dan akan pecah dalam ratusan cara jika saya mengubah ini (yang akan saya lakukan bagaimanapun).
Apa yang harus saya lakukan untuk memperbaikinya adalah membuat
NameTranslated
properti, yang akan menjadiClass B
versiName
properti dan sangat, sangat hati-hati mengubah setiap referensi keName
properti yang diturunkan untuk menggunakanNameTranslated
properti baru saya . Namun, jika salah satu dari referensi ini salah, seluruh aplikasi bisa meledak.Mengingat basis kode tidak memiliki unit test di sekitarnya, ini hampir menjadi skenario paling berbahaya yang bisa dihadapi pengembang. Jika saya tidak mengubah pelanggaran, saya harus menghabiskan banyak energi mental untuk melacak jenis objek apa yang sedang dioperasikan pada setiap metode dan jika saya memperbaiki pelanggaran, saya bisa membuat seluruh produk meledak pada waktu yang tidak tepat.
sumber
BaseName
danTranslatedName
untuk mengakses kedua gaya kelas-AName
dan makna kelas-B? Maka setiap upaya untuk mengaksesName
pada variabel tipeB
akan ditolak dengan kesalahan kompiler, sehingga Anda dapat memastikan bahwa semua referensi dikonversi ke salah satu bentuk lain.Jika Anda ingin merasakan masalah pelanggaran LSP, pikirkan apa yang terjadi jika Anda hanya memiliki .dll / .jar kelas dasar (tidak ada kode sumber) dan Anda harus membangun kelas turunan baru. Anda tidak pernah bisa menyelesaikan tugas ini.
sumber