Apakah saya mengerti benar bahwa Prinsip Pergantian Liskov tidak dapat diamati dalam bahasa di mana objek dapat memeriksa diri mereka sendiri, seperti apa yang biasa dalam bahasa yang diketik bebek?
Misalnya, di Ruby, jika kelas B
mewarisi dari kelas A
, maka untuk setiap objek x
dari A
, x.class
akan kembali A
, tetapi jika x
adalah obyek B
, x.class
tidak akan kembali A
.
Berikut ini adalah pernyataan LSP:
Biarkan q (x) menjadi dibuktikan properti tentang obyek x tipe T . Kemudian q (y) harus dibuktikan untuk benda y tipe S di mana S adalah subtipe dari T .
Jadi di Ruby, misalnya,
class T; end
class S < T; end
melanggar LSP dalam formulir ini, seperti yang disaksikan oleh properti q (x) =x.class.name == 'T'
Tambahan. Jika jawabannya "ya" (LSP tidak sesuai dengan introspeksi), maka pertanyaan saya yang lain adalah: apakah ada beberapa bentuk LSP "lemah" yang dimodifikasi yang dapat digunakan untuk bahasa yang dinamis, mungkin dalam beberapa kondisi tambahan dan hanya dengan jenis khusus dari sifat .
Memperbarui. Untuk referensi, berikut adalah formulasi lain dari LSP yang saya temukan di web:
Fungsi yang menggunakan pointer atau referensi ke kelas dasar harus dapat menggunakan objek dari kelas turunan tanpa menyadarinya.
Dan satu lagi:
Jika S adalah subtipe T yang dinyatakan, objek tipe S harus berperilaku sebagai objek tipe T yang diharapkan berperilaku, jika mereka diperlakukan sebagai objek tipe T.
Yang terakhir dijelaskan dengan:
Perhatikan bahwa LSP adalah semua tentang perilaku objek yang diharapkan. Seseorang hanya dapat mengikuti LSP jika dia jelas tentang apa perilaku objek yang diharapkan.
Ini tampaknya lebih lemah daripada yang asli, dan mungkin dapat diamati, tetapi saya ingin melihatnya diformalkan, khususnya yang menjelaskan siapa yang memutuskan apa perilaku yang diharapkan.
Apakah kemudian LSP bukan properti sepasang kelas dalam bahasa pemrograman, tetapi sepasang kelas bersama-sama dengan serangkaian properti yang diberikan, puas oleh kelas leluhur? Secara praktis, apakah ini berarti bahwa untuk membangun subclass (kelas keturunan) yang menghormati LSP, semua kemungkinan penggunaan kelas leluhur harus diketahui? Menurut LSP, kelas leluhur seharusnya bisa diganti dengan kelas turunan, kan?
Memperbarui. Saya sudah menerima jawabannya, tetapi saya ingin menambahkan satu lagi contoh nyata dari Ruby untuk mengilustrasikan pertanyaan itu. Di Ruby, setiap kelas adalah modul dalam arti bahwa Class
kelas adalah turunan dari Module
kelas. Namun:
class C; end
C.is_a?(Module) # => true
C.class # => Class
Class.superclass # => Module
module M; end
M.class # => Module
o = Object.new
o.extend(M) # ok
o.extend(C) # => TypeError: wrong argument type Class (expected Module)
Jawaban:
Inilah prinsip yang sebenarnya :
Dan ringkasan wikipedia yang luar biasa :
Dan beberapa kutipan yang relevan dari koran:
Demikian pertanyaan selanjutnya:
Tidak.
A.class
mengembalikan kelas.B.class
mengembalikan kelas.Karena Anda dapat melakukan panggilan yang sama pada jenis yang lebih spesifik dan mendapatkan hasil yang kompatibel, LSP menahan. Masalahnya adalah bahwa dengan bahasa dinamis, Anda masih dapat memanggil hal-hal pada hasil yang mengharapkan mereka ada di sana.
Tapi mari kita pertimbangkan bahasa yang diketik secara statis, struktural (bebek). Dalam hal ini,
A.class
akan mengembalikan tipe dengan batasan yang harusA
atau subtipeA
. Ini memberikan jaminan statis bahwa subtipe apa punA
harus menyediakan metodeT.class
yang hasilnya adalah jenis yang memenuhi kendala itu.Ini memberikan pernyataan yang lebih kuat yang dimiliki LSP dalam bahasa yang mendukung pengetikan bebek, dan bahwa setiap pelanggaran LSP dalam sesuatu seperti Ruby terjadi lebih karena penyalahgunaan dinamis normal daripada ketidakcocokan desain bahasa.
sumber
fail unless x.foo == 42
dan subtipe mengembalikan 0 itu hal yang sama. Ini bukan kegagalan LSP, ini adalah operasi normal program Anda. Polimorfisme bukanlah pelanggaran terhadap LSP.Dalam konteks LSP "properti" adalah sesuatu yang dapat diamati pada suatu tipe (atau objek). Secara khusus ia berbicara tentang "properti yang dapat dibuktikan".
"Properti" seperti itu dapat memiliki
foo()
metode yang tidak memiliki nilai pengembalian (dan mengikuti kontrak yang ditetapkan dalam dokumentasinya).Pastikan Anda tidak mengacaukan istilah ini dengan "properti" seperti pada "
class
adalah properti dari setiap objek di Ruby". Seperti sebuah "properti" bisa menjadi "LSP properti", tapi itu tidak secara otomatis sama!Sekarang jawaban atas pertanyaan Anda sangat tergantung pada seberapa ketat Anda mendefinisikan "properti". Jika Anda mengatakan "milik kelas
A
adalah bahwa.class
akan kembali jenis objek", makaB
sebenarnya tidak memiliki properti itu.Namun, jika Anda mendefinisikan "properti" menjadi "
.class
kembaliA
", maka jelasB
tidak tidak memiliki properti itu.Namun, definisi kedua tidak terlalu berguna, karena pada dasarnya Anda telah menemukan cara bulat untuk mendeklarasikan konstanta.
sumber
.class
akan mengembalikan jenis objek". Jika itu berartix.class == x.class
, ini bukan properti yang menarik.Seperti yang saya pahami, tidak ada apapun tentang introspeksi yang tidak sesuai dengan LSP. Pada dasarnya, selama suatu objek mendukung metode yang sama seperti yang lain, keduanya harus dipertukarkan. Artinya, jika kode Anda mengharapkan
Address
objek, maka tidak peduli apakah ituCustomerAddress
atauWarehouseAddress
, asalkan keduanya memberikan (misalnya)getStreetAddress()
,getCityName()
,getRegion()
dangetPostalCode()
. Anda tentu bisa membuat beberapa jenis dekorator yang mengambil berbagai jenis objek dan menggunakan introspeksi untuk memberikan metode yang diperlukan (misalnya,DestinationAddress
kelas yang mengambilShipment
objek dan menyajikan kapal-ke alamat sebagaiAddress
), tetapi itu tidak diperlukan dan tentu saja tidak tidak dapat mencegah LSP diterapkan.sumber
x.class.name
dengan'A'
, secara efektif membuatx.class.name
tidak berguna .x.class.name == 'A'
anti-pola dalam mengetik bebek: setelah semua, mengetik bebek berasal dari "Jika dukun dan berjalan seperti bebek, itu bebek". Jadi jika itu berperilaku sepertiA
dan menghormati kontrak yangA
diajukan, itu adalahA
contohSetelah membaca makalah asli Barbara Liskov, saya menemukan cara untuk melengkapi definisi Wikipedia sehingga LSP benar-benar dapat dipenuhi dalam hampir semua bahasa.
Pertama-tama, kata "dapat dibuktikan" penting dalam definisi. Itu tidak dijelaskan dalam artikel Wikipedia, dan "kendala" disebutkan di tempat lain tanpa referensi.
Inilah kutipan penting pertama dari makalah ini:
Dan inilah yang kedua, menjelaskan apa itu spesifikasi tipe :
Jadi, LSP hanya masuk akal berkenaan dengan spesifikasi tipe yang diberikan , dan untuk spesifikasi tipe yang sesuai (untuk yang kosong misalnya), dapat dipenuhi dalam bahasa apa pun.
Saya menganggap jawaban Telastyn sebagai yang paling dekat dengan apa yang saya cari karena "kendala" disebutkan secara eksplisit.
sumber
x.class.name = 'A'
ini terbukti untuk semuax
kelasA
jika Anda mengizinkan terlalu banyak pengetahuan. The tipe spesifikasi tidak didefinisikan, dan hubungan yang tepat dengan LSP tidak baik, meskipun informal beberapa indikasi diberikan. Saya telah menemukan apa yang saya cari di koran Liskov dan menjawab pertanyaan saya di atas.x
itu,x.woozle
akan menghasilkanundefined
, maka tidak ada jenis yangx.woozle
tidak menghasilkanundefined
akan menjadi subtipe yang tepat. Jika supertype tidak mendokumentasikan apa pun tentangx.woozle
, fakta bahwa penggunaanx.woozle
pada supertype akan menghasilkan tidakundefined
akan menyiratkan apa-apa tentang apa yang mungkin dilakukan pada subtipe.Mengutip artikel Wikipedia tentang LSP , "substitusi adalah prinsip dalam pemrograman berorientasi objek." Ini adalah prinsip dan bagian dari desain program Anda. Jika Anda menulis kode yang tergantung
x.class == A
, maka kode Anda yang melanggar LSP. Perhatikan bahwa kode rusak semacam ini juga dimungkinkan di Jawa, tidak perlu mengetik bebek.Tidak ada dalam mengetik bebek yang secara inheren merusak LSP. Hanya jika Anda menyalahgunakannya, seperti dalam contoh Anda.
Pemikiran tambahan: bukankah secara eksplisit memeriksa kelas suatu objek mengalahkan tujuan dari mengetik bebek, toh?
sumber
Liskov's notion of a behavioral subtype defines a notion of substitutability for mutable objects; that is, if S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program (e.g., correctness).
. Kelas yang tepat dari suatu objek BUKAN salah satu dari "sifat yang diinginkan dari program"; jika tidak, hal itu akan bertentangan dengan tidak hanya mengetik bebek tetapi subtipe secara umum, termasuk rasa Jawa.x.class == A
melanggar LSP dan mengetik bebek. Tidak ada gunanya menggunakan bebek mengetik jika Anda akan memeriksa jenis yang sebenarnya.A
,B
) memenuhi LSP atau tidak. Jika LSP tergantung pada kode yang digunakan di tempat lain, tidak dijelaskan kode apa yang diizinkan. Saya berharap menemukan sesuatu di sini: cse.ohio-state.edu/~neelam/courses/788/lwb.pdfLet q(x) be a property provable about objects x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of T
. Sudah jelas bahwax.class
ini bukan salah satu properti yang menarik di sini; jika tidak , polimorfisme Jawa juga tidak akan berfungsi. Tidak ada yang melekat pada bebek mengetik dalam "masalah x.class" Anda. Apakah Anda setuju sejauh ini?