Liskov İkame İlkesi İntrospeksiyon veya Ördek Tipleme ile uyumlu değil mi?


11

Ördek yazılan dillerde olağan gibi , Liskov İkame İlkesinin nesnelerin kendilerini denetleyebileceği dillerde gözlenemediğini doğru anladım mı?

Örneğin, Ruby, eğer bir sınıf Bbir sınıftan devralır A, sonra her nesne xarasında A, x.classdönüş gidiyor A, ama eğer xbir amacı B, x.classgeri gitmiyor A.

İşte LSP'nin bir açıklaması:

Let q (X) nesneler hakkında bir özellik ispat edilebilir x tipi T . Daha sonra q, (y) nesneler için kanıtlanabilir olmalıdır y Çeşidi S burada G bir alt tipi olan T .

Yani Ruby'de, örneğin,

class T; end
class S < T; end

q (x) = özelliğinin tanığı gibi LSP'yi bu biçimde ihlal etmekx.class.name == 'T'


İlave. Cevap "evet" ise (LSP içgözlemle uyumlu değildir), diğer sorum şu olabilir: muhtemelen bazı ek koşullar altında ve yalnızca özel türlerle dinamik bir dil tutabilecek bazı değiştirilmiş "zayıf" LSP formu var mı? ait özellikleri .


Güncelleme. Referans için, web'de bulduğum başka bir LSP formülasyonu:

İşaretçiler veya temel sınıflara başvurular kullanan işlevler, türetilmiş sınıfların nesnelerini bilmeden kullanabilmelidir.

Ve başka:

S, T'nin beyan edilen bir alt tipi ise, T tipi nesnelerin, T tipi nesneler olarak muamele gördükleri takdirde, T tipi nesnelerin davranması beklendiği için davranmalıdır.

Sonuncusu şu açıklamalara eklenir:

LSP'nin tamamen nesnelerin beklenen davranışı ile ilgili olduğunu unutmayın. Bir kişi LSP'yi ancak nesnelerin beklenen davranışının ne olduğu konusunda açıksa izleyebilir.

Bu orijinalinden daha zayıf gibi gözüküyor ve gözlemlemek mümkün olabilir, ancak bunu resmi olarak görmek istiyorum, özellikle beklenen davranışın ne olduğuna kimin karar verdiğini açıkladı.

Öyleyse LSP, bir programlama dilinde bir çift sınıfın özelliği değil, bir çift sınıfın bir dizi özellikle birlikte, ata sınıfı tarafından memnun mu? Pratik olarak, bu LSP'ye saygı duyan bir alt sınıf (alt sınıf) oluşturmak için, ata sınıfının tüm olası kullanımlarının bilinmesi gerektiği anlamına mı gelir? LSP'ye göre, ata sınıfının herhangi bir alt sınıfla değiştirilmesi gerekiyordu, değil mi?


Güncelleme. Cevabı zaten kabul ettim, ancak soruyu göstermek için Ruby'den bir somut örnek daha eklemek istiyorum. Ruby'de her sınıf, Classsınıfın sınıfın soyundan gelmesi anlamında bir modüldür Module. Ancak:

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)

2
Hemen hemen tüm modern diller bir dereceye kadar içgözlem sağlar, bu yüzden soru Ruby'ye özgü değildir.
Joachim Sauer

Anlıyorum, Ruby'yi örnek olarak verdim. Bilmiyorum, belki bazı diğer dillerde içgözlem ile LSP'nin bazı "zayıf formları" vardır, ancak, prensibi doğru bir şekilde anladıysam, içgözlemle bağdaşmaz.
Alexey

"Ruby" i başlıktan kaldırdım.
Alexey

2
Kısa cevap, uyumlu olmalarıdır. İşte en çok
katıldığım

2
@Alexey Bu bağlamdaki özellikler bir nesnenin değişmezleridir. Örneğin, değiştirilemeyen nesneler değerlerinin değişmeme özelliğine sahiptir. İyi birim testlerine bakarsanız, bu özellikleri tam olarak test etmelidirler.
K.Steff

Yanıtlar:


29

İşte asıl ilke :

Izin vermek türü q(x)nesneler için kanıtlanabilir bir özellik olsun . Sonra nesneler için kanıtlanabilir olmalıdır Çeşidi nerede bir tipidir .xTq(y)ySST

Ve mükemmel wikipedia özeti:

Bir bilgisayar programında, S, T'nin bir alt tipiyse, T tipi nesnelerin, S tipi nesnelerle (yani, S tipi nesnelerin herhangi birisini değiştirmeden değiştirilebileceğini) belirtir. o programın istenen özellikleri (doğruluk, yapılan görev, vb.).

Ve makaleden bazı alıntılar:

İhtiyaç duyulan şey, alt türlerin davranışını sınırlayan daha güçlü bir gereksinimdir: nesne aslında bu tür bir alt türün üyesi olsa bile , bir nesnenin varsayılan türünün belirtimi kullanılarak kanıtlanabilir özellikler ...

Bir tür belirtimi aşağıdaki bilgileri içerir:
- Türün adı;
- Türün değer alanının açıklaması;
- Türün yöntemlerinin her biri için:
--- Adı;
--- İmzası (bildirilen istisnalar dahil);
--- Ön koşullar ve son koşullar açısından davranışı.

Soruya devam edelim:

Ördek yazılan dillerde olağan gibi, Liskov İkame Prensibi'nin nesnelerin kendilerini denetleyebileceği dillerde gözlenemediğini doğru anladım mı?

Hayır.

A.classbir sınıf döndürür.
B.classbir sınıf döndürür.

Aynı aramayı daha spesifik bir tipte yapabileceğiniz ve uyumlu bir sonuç alabileceğiniz için LSP bekletir. Sorun şu ki, dinamik dillerde, sonuçta orada olmalarını bekleyen şeyleri çağırabilirsiniz.

Ancak statik, yapısal (ördek) bir dili düşünelim. Bu durumda, A.classolması gereken bir kısıtlamaya sahip Abir türü veya alt türünü döndürür A. Bu, herhangi bir alt türünün , sonucu bu kısıtlamayı karşılayan bir tür olan Abir yöntem sağlaması gerektiğine dair statik garanti sağlar T.class.

Bu, LSP'nin ördek yazmayı destekleyen dillerde tuttuğu ve Ruby gibi bir şeyde LSP'nin herhangi bir ihlali, normal dinamik kötüye kullanım nedeniyle bir dil tasarım uyumsuzluğundan daha fazla gerçekleştiğini daha güçlü bir iddia sağlar.


1
"Aynı aramayı daha spesifik bir tipte yapabileceğiniz ve uyumlu bir sonuç alabileceğiniz için, LSP bekletiyor" Eğer doğru anladıysam LSP sonuçları aynı ise tutar. Belki de sadece belirli kısıtlamaların yerine getirilmesi gereken tüm özelliklerin yerine, belirli kısıtlamalarla ilgili olarak bir "hafta" LSP formu olabilir. Her durumda, herhangi bir referansı takdir ediyorum.
Alexey

@Alexey, LSP'nin ne anlama geldiğini içerecek şekilde düzenlendi. B'yi A'yı beklediğim yerde kullanabilirsem LSP tutar. Ruby'nin .class'ının bunu nasıl ihlal ettiğini düşündüğünü merak ediyorum.
Telastyn

3
@Alexey - Programınız içeriyorsa fail unless x.foo == 42ve bir alt tür 0 döndürüyorsa, bu aynı şeydir. Bu bir LSP hatası değil, programınızın normal çalışması. Polimorfizm LSP'nin ihlali değildir.
Telastyn

1
@Alexey - Tabii. Bir mülk olduğunu varsayalım. Bu durumda, alt türlerin aynı semantik davranışa sahip olmasına izin vermediğinden, kodunuz LSP'yi ihlal etmez. Ancak, dinamik veya ördek türünde diller için özel değildir. Dil tasarımlarında ihlale neden olan hiçbir şey yapmıyorlar. Yazdığınız kod. Unutmayın, LSP, programların matematiksel bir özelliğinden ziyade bir program tasarımı ilkesidir (dolayısıyla tanımda olması gerekir ).
Telastyn

6
@Alexey: x.class == A'ya bağlı bir şey yazarsanız , dili değil, LSP'yi ihlal eden kodunuzdur . Hemen hemen her programlama dilinde LSP'yi ihlal eden kodlar yazmak mümkündür.
Andres F.

7

LSP bağlamında bir "özellik", bir tür (veya nesne) üzerinde gözlemlenebilecek bir şeydir. Özellikle bir "kanıtlanabilir mülk" hakkında konuşuyor.

Böyle bir "mülkiyet", foo()dönüş değeri olmayan bir yöntemin varlığı olabilir (ve belgelerinde belirtilen sözleşmeyi izler).

" classRuby'deki her nesnenin bir özelliği " olduğu için bu terimi "özellik" ile karıştırmamaya dikkat edin . Böyle bir "özellik" bir "LSP özelliği" olabilir, ancak otomatik olarak aynı değildir!

Şimdi sorularınızın cevabı, "mülkiyet" i ne kadar katı tanımladığınıza bağlıdır. Eğer "sınıfının özelliği Aolmasıdır .classnesnenin türünü döndürür", daha sonra Baslında yapar bu özelliği vardır.

Bununla birlikte, "özellik" i " .classdöndürür A" olarak tanımlarsanız, açıkça Bbu özelliğe sahip olmazsınız .

Bununla birlikte, esas olarak bir sabit beyan etmenin yuvarlak bir yolunu bulduğunuz için, ikinci tanım çok yararlı değildir.


Bir programın "özelliğinin" yalnızca bir tanımını düşünebilirim: belirli bir girdi için belirli bir değer döndürür veya daha genel olarak, başka bir programda blok olarak kullanıldığında, belirli bir girdi için diğer program bir döndürür verilen değerler. Bu tanım ile, " .classnesnenin türünü döndürür " ne anlama geldiğini görmüyorum . Öyle ise x.class == x.class, bu ilginç bir özellik değildir.
Alexey

1
@Alexey: Sorumumu LSP bağlamında "mülkiyet" in ne anlama geldiğine dair bir açıklama ile güncelledim.
Joachim Sauer

2
@Alexey: makaleye baktığımda belirli bir tanım veya "özellik" bulamıyorum. Muhtemelen bu terim genel CS anlamında "bir nesne hakkında gözlemlenebilen / kanıtlanabilecek bir şey" anlamında kullanıldığından kaynaklanmaktadır. Bunun diğer eteğin "bir nesnenin alanı" ile ilgisi yoktur.
Joachim Sauer

4
@Alexey: Sana daha ne söyleyebileceğimi bilmiyorum. "Bir özellik bir nitelik veya bir nesnenin özniteliğidir " tanımını kullanıyorum . "renk" fiziksel, görünür bir nesnenin bir özelliğidir . "yoğunluk" bir malzemenin özelliğidir . "belirtilen bir yönteme sahip" bir sınıfın / nesnenin bir özelliğidir .
Joachim Sauer

4
@Alexey: Bence bebeği banyo suyuyla atıyorsunuz: Bazı özellikler için LSP'nin tutulamayacağı için işe yaramaz olduğu veya "herhangi bir dilde tutulmadığı " anlamına gelmiyor. Ancak bu tartışma burada çok ileri gider.
Joachim Sauer

5

Anladığım kadarıyla, içgözlemle ilgili LSP ile uyumsuz hiçbir şey yok. Temel olarak, bir nesne diğeriyle aynı yöntemleri desteklediği sürece, ikisi birbirinin yerine geçebilir olmalıdır. Kodunuz bir bekliyorsa olduğunu Yani, Addressnesneyi, o zaman bunun ise önemli değil CustomerAddressya da WarehouseAddresssürece her ikisi için de, (örneğin) getStreetAddress(), getCityName(), getRegion()ve getPostalCode(). Farklı türde bir nesne alan ve gerekli yöntemleri sağlamak için içgözlem kullanan bir tür dekoratör kesinlikle oluşturabilirsiniz (örneğin, DestinationAddressbir Shipmentnesneyi alan ve gönderim adresini bir olarak sunan bir sınıf Address), ancak gerekli değildir ve kesinlikle olmaz LSP'nin uygulanmasını engellemeyin.


2
@Alexey: Nesneler aynı yöntemleri destekliyorsa "aynı" dır. Bu, aynı ad, aynı sayıda ve türde bağımsız değişken, aynı döndürme türü ve aynı yan etkiler anlamına gelir (çağrı kodu tarafından görülebildiği sürece). Yöntemler tamamen farklı davranabilir, ancak sözleşmeyi onurlandırdıkları sürece, sorun değil.
TMN

1
@Alexey: ama neden böyle bir sözleşmem var? Bu sözleşme hangi gerçek kullanımı yerine getiriyor? Böyle bir sözleşme olsaydı ben sadece her faydalarının gelişi yerini alabilecek x.class.nameile 'A' etkin bir şekilde yaparak, x.class.name işe yaramaz .
Joachim Sauer

1
@Alexey: tekrar: başka bir sınıfı genişleterek yerine getirilemeyen bir sözleşme tanımlayabilmeniz LSP'yi bozmaz. Bu sadece genişletilemez bir sınıf oluşturduğunuz anlamına gelir. " Sağlanan kod bloğu sonlu sürede biterse " dönmek için bir yöntem tanımlarsanız o zaman da yerine getirilemez bir sözleşme var. Bu, programlamanın faydasız olduğu anlamına gelmez.
Joachim Sauer

2
@Alexey x.class.name == 'A', ördek yazımında bir anti-desen olup olmadığını belirlemeye çalışıyor : sonuçta, ördek yazarak " Ördekleri fırlatır ve ördek gibi yürürse , bir ördek" den gelir. Öyleyse ortaya Açıkan sözleşmeler gibi davranır ve onurlandırılırsa A, bu bir Aörnektir
K.Steff

1
@Alexey Size net tanımlar verildi. Bir nesnenin sınıfı, davranışının veya sözleşmesinin ya da onu adlandırmak istediğiniz herhangi bir şeyin parçası değildir. "Özellik" i, işaret edildiği gibi x.myField gibi bir nesne alanı ile eşleştiriyorsunuz, aynı DEĞİLDİR. Bu bağlamda, bir özellik daha çok tür değişmezleri gibi matematiksel bir özelliğe benzer. Ayrıca, ördek yazmayı istiyorsanız tam tipini kontrol etmek için bir anti-desen. Peki, yine LSP ve ördek yazmayla ilgili sorununuz nedir? ;)
Andres F.30

4

Barbara Liskov'un orijinal makalesine baktıktan sonra, LSP'nin neredeyse her dilde tatmin olabilmesi için Wikipedia tanımını nasıl tamamlayacağımı buldum.

Her şeyden önce tanımda "kanıtlanabilir" kelimesi önemlidir. Wikipedia makalesinde açıklanmamıştır ve "kısıtlamalar" başka bir yerde kaynak gösterilmeden bahsedilmiştir.

Makaleden ilk önemli alıntı:

İhtiyaç duyulan şey, alt türlerin davranışını kısıtlayan daha güçlü bir gereksinimdir: nesne aslında bu tür bir alt türün üyesi olsa bile , bir nesnenin varsayılan türünün belirtimi kullanılarak kanıtlanabilir özellikler ...

Ve işte ikincisi, bir tür belirtiminin ne olduğunu açıklayan :

Bir tür belirtimi aşağıdaki bilgileri içerir:

  • Türün adı;
  • Türün değer alanının açıklaması;
  • Türün yöntemlerinin her biri için:
    • Onun adı;
    • İmzası (belirtilen istisnalar dahil);
    • Ön koşullar ve son koşullar açısından davranışı.

Bu nedenle, LSP sadece verilen tip spesifikasyonlarına göre mantıklıdır ve uygun bir tip spesifikasyonu için (örneğin boş olan için), muhtemelen herhangi bir dilde tatmin edilebilir.

Ben düşünün Telastyn cevabı ben "kısıtlamalar" zikredilmiş çünkü aradığı şeyi en yakın olması.


Telastyn, bu alıntıları cevabınıza ekleyebilseydiniz, sizinkini kendiminkinden kabul etmeyi tercih ederim.
Alexey

2
işaretleme tamamen aynı değil ve ben anfeziden biraz değiştirdim, ancak alıntılar dahil edildi.
Telastyn

Maalesef, Joachim Sauer, beğenmediğiniz "kanıtlanabilir" mülklerden zaten bahsetti. Genel olarak, sadece mevcut cevapları yeniden ifade ettiniz. Dürüst olmak gerekirse, ne aradığını bilmiyorum ...
Andres F.

Hayır, "neyden kanıtlanabilir?" Çok fazla bilgi izin verirseniz özelliği x.class.name = 'A'tüm xsınıf için kanıtlanabilir A. Tipi belirleme tanımlanmamış ve gayri bazı göstergeler verilmiştir da LSP ile tam bir ilişki, ya değildi. Aradığımı Liskov'un gazetesinde buldum ve yukarıdaki soruma cevap verdim.
Alexey

Bence cesaretlendirdiğin kelimeler anahtar. Biri için yapılacak süpertip belgeler varsa xbu tür, x.woozleverecektir undefined, o zaman hiçbir hangi tip x.woozleverim vermez undefineduygun bir alt tip olacaktır. Süper tip hakkında hiçbir şey belgelemezse , süper tipte kullanımın sonuç vereceği x.woozlegerçeği , alt tipte ne yapabileceği konusunda hiçbir şey ifade etmeyecektir. x.woozleundefined
supercat

3

Wikipedia'nın LSP ile ilgili makalesinde alıntı yapmak için "ikame edilebilirlik, nesne yönelimli programlamada bir ilkedir." Bu, programınızın tasarımının bir ilkesi ve parçasıdır. Buna bağlı bir kod yazarsanız x.class == A, LSP'yi ihlal eden kodunuzdur . Bu tür kırık kodların Java'da da mümkün olduğunu unutmayın, ördek yazmaya gerek yoktur.

Ördek yazarken hiçbir şey doğal olarak LSP'yi kırmaz. Sadece örneğinizde olduğu gibi kötüye kullanırsanız.

Ek düşünce: bir nesnenin sınıfını açıkça kontrol etmek, yine de ördek yazmanın amacını bozmuyor mu?


Andres, LSP tanımınızı verir misiniz, lütfen?
Alexey

1
@Alexey LSP'nin kesin tanımı Wikipedia'da alt türler olarak belirtilmiştir. Gayri resmi tanım 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).. Bir nesnenin tam sınıfı "programın istenen özelliklerinden" biri DEĞİLDİR; Aksi takdirde sadece ördek tipine değil, genel olarak alt tiplemeye de aykırıdır, Java'nın lezzeti de buna dahildir.
Andres F.30

2
@Alexey Ayrıca senin örneğin unutmayın x.class == ALSP'yi hem ihlal ve ördek yazmaya. Gerçek türleri kontrol edecekseniz, ördek yazmayı kullanmanın bir anlamı yok.
Andres F.

Andres, bu tanım anlayabileceğim kadar kesin değil. Hangi program, belirli bir program veya herhangi bir program? Arzu edilen özellik nedir? Sınıf bir kütüphanedeyse, farklı uygulamalar farklı özellikleri tercih edebilir. Bir kod satırı nasıl LSP ihlal olabilir görmüyorum, çünkü LSP verilen bir programlama dilinde sınıfların bir çift bir özellik olduğunu düşündüm: ya ( A, B) LSP karşılamak ya da değil. LSP başka bir yerde kullanılan koda bağlıysa, hangi koda izin verildiği açıklanmaz. Burada bir şey bulmayı umuyorum: cse.ohio-state.edu/~neelam/courses/788/lwb.pdf
Alexey

2
@Alexey LSP, belirli bir tasarım için geçerlidir (veya içermez). Bir tasarımda aranacak bir şeydir; genel olarak bir dilin malı değildir. Bu gerçek tanımı daha başka kesin almaz: Let 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. x.classBuradaki ilginç özelliklerden biri olmadığı açıktır ; aksi halde Java'nın polimorfizmi de işe yaramaz. Orada hiçbir şey senin "x.class sorunu" ördek yazmaya doğasında. Şimdiye kadar katılıyor musun?
Andres F.30
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.