Durum Paterni Liskov İkame İlkesini ihlal ediyor mu?


17

Bu görüntü Etki Alanına Dayalı Tasarım ve Desenler Uygulamadan alınmıştır : C # ve .NET'teki Örneklerle

resim açıklamasını buraya girin

Bu sınıf diyagramı olan Devlet Kalıbı bir SalesOrderyaşam süresi boyunca farklı durum olabilir. Farklı durumlar arasında sadece belirli geçişlere izin verilir.

Şimdi OrderStatesınıf bir abstractsınıftır ve tüm yöntemleri alt sınıflarına devralınır. Başka Cancelledbir ülkeye geçiş yapılmasına izin vermeyen bir son durum olan alt sınıfı ele alırsak override, bu sınıftaki tüm yöntemlerinin istisnalar atması gerekir.

Şimdi bu, Liskov'un ikame ilkesini ihlal etmiyor çünkü bir sublcass ebeveynin davranışını değiştirmemeli mi? Soyut sınıfı bir arayüze dönüştürmek bunu düzeltir mi?
Bu nasıl düzeltilebilir?


OrderState'i varsayılan olarak tüm yöntemlerinde "NotSupported" özel durumları atan somut bir sınıf olarak değiştirilsin mi?
James

Bu kitabı okumadım, ancak OrderState'in Cancel () içerdiği ve sonra İptal edildi durumu var, bu farklı bir alt tip olduğu için garip görünüyor.
Euphoric

@Euphoric neden garip? çağrı cancel(), iptal edilen durumu değiştirir.
Songo

Nasıl? SalesOrder'daki durum örneğinin değişmesi gerektiğinde OrderState'te İptal'i nasıl çağırabilirsiniz? Bence bütün model yanlış. Tam uygulamayı görmek istiyorum.
Euphoric

Yanıtlar:


3

Bu özel uygulama, evet. Eğer devletleri soyut uygulayıcılardan ziyade somut sınıflar yaparsanız bundan kurtulacaksınız.

Bununla birlikte, etkili bir şekilde bir devlet makinesi tasarımı olan bahsettiğiniz devlet modeli, genel olarak, büyüdüğünü gördüğümden katılmadığım bir şeydir. Bence bunu tek sorumluluk ilkesinin ihlali olarak çözmek için yeterli zemin var çünkü bu devlet yönetimi kalıpları bir sistemin diğer birçok bölümünün mevcut durumu hakkında bilgi için merkezi depolar haline geliyor. Merkezileştirilen bu devlet yönetimi parçası, sistemin makul bir şekilde koordine edilmesi için sistemin birçok farklı bölümüyle ilgili iş kurallarını gerektirmeyecektir.

Sistemin devlete önem veren tüm bölümlerinin farklı hizmetlerde, farklı makinelerde farklı süreçlerde, bu yerlerin her birinin durumunu ayrıntılı olarak anlatan bir merkezi durum yöneticisi olup olmadığını, tüm bu dağıtılmış sistemin etkili bir şekilde tıkanıklığını kontrol ettiğini ve bence darboğazın bir işaret olduğunu düşünün SRP ihlali ve genellikle kötü tasarım.

Buna karşılık, bir modelin kendisini nasıl ele alacağını bildiği MVC modelindeki Model nesnesindeki gibi nesneleri daha akıllı hale getirmesini öneririm, dahili işlerini veya bunun nedenini yönetmek için dış bir orkestratöre ihtiyaç duymaz.

Bir nesnenin içine böyle bir durum paterni yerleştirmek bile, sadece kendini yönetmesi için, o nesneyi çok büyük yapıyormuşsunuz gibi hissettirir. İş akışları, diğer nesnelerin akışını veya kendi içindeki zeka akışını yöneten tek bir orkestre durum yerine, söyleyeceğim çeşitli kendinden sorumlu nesnelerin bileşimi yoluyla yapılmalıdır.

Ama bu noktada mühendislik daha sanat ve kesinlikle söz konusu şeylerden bazıları yaklaşımınızı, sübjektif bu nedenle ilkeler iyi bir rehber ve evet uygulama liste olan bir LSP ihlali ancak olmamak için düzeltilebilir. Bu türden herhangi bir desen kullanırken SRP konusunda çok dikkatli olun ve muhtemelen güvende olacaksınız.


Temel olarak, büyük sorun, nesne yöneliminin yeni sınıflar eklemek için iyi olması, ancak yeni yöntemler eklemeyi zorlaştırmasıdır. Ve devlet durumunda, dediğin gibi, kodu yeni eyaletlerle çok sık genişletmeniz gerekmeyebilir.
Ocak'ta hugomg

3
@Jimmy Hoffa Üzgünüz, ama tam olarak ne demek istiyorsun "Eğer devletleri soyut uygulayıcılardan ziyade somut sınıflar yaparsanız, bundan uzaklaşacaksınız." ?
Songo

@Jimmy: Her bir bileşenin yalnızca kendi durumunu bilmesini sağlayarak, devlet modeli olmadan devleti uygulamayı denedim. Bu, büyük akış değişikliklerinin uygulanması ve sürdürülmesi için önemli ölçüde daha karmaşık hale geldi. Bununla birlikte, devlet tasarım modelinden ziyade bir devlet makinesi (ideal olarak bir başkasının kütüphanesi bir kara kutu olarak muamele görmeye zorlamak) kullanmayı düşünüyorum (bu nedenle, devletler tam sınıflar yerine sadece bir numaralandırma içindeki öğelerdir ve geçişler sadece aptaldır) kenarlar) geliştiriciler tarafından kötüye kullanma girişimlerine düşmanken devlet modelinin birçok faydasını verir.
Brian

8

bir sublcass ebeveynin davranışını değiştirmemelidir?

Bu LSP'nin yaygın olarak yanlış yorumlanmasıdır. Bir alt sınıf, üst türe sadık kaldığı sürece üst öğenin davranışını değiştirebilir.

Wikipedia'da LSP'yi ihlal edecek şeyleri öneren iyi bir uzun açıklama var :

... alt türün karşılaması gereken bir takım davranış koşulları vardır. Bunlar, sözleşme yöntemiyle tasarıma benzeyen bir terminolojide ayrıntılı olarak anlatılmıştır ve sözleşmelerin mirasla nasıl etkileşime girebileceği konusunda bazı kısıtlamalara yol açmaktadır:

  • Bir alt tipte önkoşullar güçlendirilemez.
  • Alt koşullar bir alt türde zayıflatılamaz.
  • Süpertipin değişmezleri bir alt tipte korunmalıdır.
  • Geçmiş kısıtı ("geçmiş kuralı"). Nesneler yalnızca yöntemleri (kapsülleme) yoluyla değiştirilebilir olarak kabul edilir. Alt tipler, üst tipte bulunmayan yöntemler getirebileceğinden, bu yöntemlerin eklenmesi, alt tipte üst tipte izin verilmeyen durum değişikliklerine izin verebilir. Tarih kısıtı bunu yasaklar. Liskov ve Wing tarafından tanıtılan yeni elementti. Bu kısıtlamanın ihlali, bir MutablePoint'in bir ImmutablePoint'in alt türü olarak tanımlanmasıyla örneklenebilir. Bu, tarih kısıtlamasının ihlalidir, çünkü Değişmez noktanın tarihinde, devlet yaratıldıktan sonra her zaman aynıdır, bu nedenle genel olarak bir MutablePoint'in tarihini içeremez. Bununla birlikte, alt tipe eklenen alanlar, üst tip yöntemlerle gözlemlenemedikleri için güvenli bir şekilde değiştirilebilir.

Şahsen, bunu hatırlamak daha kolay buluyorum: A tipi bir yöntemde bir parametreye bakıyorsam, bir B alt tipini geçen birisi bana herhangi bir sürpriz yapar mı? Eğer yaparlarsa LSP ihlali olur.

İstisna atmak sürpriz midir? Pek sayılmaz. Bu, OrderState'de veya Grated veya Shipped'de Ship yöntemini çağırıyor olsam da, herhangi bir zamanda olabilecek bir şey. Bu yüzden bunu hesaba katmam gerekiyor ve bu aslında LSP'nin bir ihlali değil.

Bununla birlikte , bu durumu ele almanın daha iyi yolları olduğunu düşünüyorum. Bu C # yazıyor olsaydım, arabirimleri kullanmak ve yöntemi çağırmadan önce bir arabirimin uygulanmasını kontrol ediyorum. Örneğin, geçerli OrderState IShippable'ı uygulamıyorsa, üzerinde bir Ship yöntemi çağırmayın.

Fakat o zaman bu özel durum için Devlet modelini de kullanmam. Durum modeli, böyle bir etki alanı nesnesinin durumundan çok bir uygulamanın durumuna göre daha uygundur.

Yani, kısaca, bu Devlet Paterninin zayıf bir şekilde ele geçirilmiş bir örneğidir ve bir emrin durumunu ele almanın özellikle iyi bir yolu değildir. Ancak tartışmasız LSP'yi ihlal etmiyor. Ve Devlet modeli, kendi başına, kesinlikle değildir.


Bana LSP'yi en az sürpriz / şaşkınlık ilkesi ile karıştırıyormuşsunuz gibi geliyor, sanırım LSP'nin, onları ihlal ettiği yerlerde daha net sınırlara sahip olmasına rağmen, öznel beklentileri temel alan daha öznel POLA'yı uyguluyorsunuz. ; yani her insan bir sonrakinden farklı bir şey bekler. LSP, tüketici tarafından tahmin edilen beklentileri sübjektif olarak değerlendirmeyen, sağlayıcı tarafından verilen sözleşmeye dayalı ve iyi tanımlanmış garantilere dayanmaktadır.
Jimmy Hoffa

@JimmyHoffa: Haklısın, bu yüzden LSP'yi hatırlamanın daha basit bir yolunu belirtmeden önce tam anlamını söyledim. Ancak, aklımda "sürpriz" ne demek bir soru varsa, o zaman geri dönüp LSP'nin kesin kurallarını kontrol edeceğim (gerçekten onları hatırlayabilir misiniz?). İşlevselliği değiştiren istisnalar (ya da tam tersi) zordur, çünkü yukarıdaki herhangi bir maddeyi ihlal etmez, bu muhtemelen tamamen farklı bir şekilde ele alınması gerektiğine dair bir ipucudur.
pdr

1
Bir istisna, aklımdaki sözleşmede tanımlanan beklenen bir durumdur, bir NetworkSocket düşünün, açık değilse gönderme konusunda beklenen bir istisna olabilir, uygulamanız kapalı bir soket için bu istisnayı atmazsa LSP'yi ihlal ediyorsunuz , sözleşme aksini belirtirse ve alt türünüz istisnayı atarsa ​​LSP'yi ihlal edersiniz. LSP'deki istisnaları şu şekilde sınıflandırıyorum. Kuralları hatırlamaya gelince; Bu konuda yanlış olabilirim ve bunu bana söyleyebilirim, ancak LSP vs POLA hakkındaki anlayışım yukarıdaki yorumumun son cümlesinde tanımlanıyor.
Jimmy Hoffa

1
@JimmyHoffa: POLA ve LSP'nin uzaktan bile bağlı olduğunu hiç düşünmemiştim (POLI'yi UI terimleriyle düşünüyorum) ama şimdi belirttiğinize göre, bunlar bir nevi. Yine de çatışma içinde olduklarından ya da birinin diğerinden daha öznel olduğundan emin değilim. LSP, yazılım mühendisliği açısından POLA'nın erken bir tanımı değil mi? Ctrl-C Kopyalama (POLA) olmadığında hayal kırıklığına uğradığım gibi, aslında bir MutablePoint alt sınıfı (LSP) olduğu için bir ImmutablePoint değiştirilebilir olduğunda da hayal kırıklığına uğrarım.
pdr

1
CircleWithFixedCenterButMutableRadiusTüketici sözleşmesi , tüketicilerin X'i serbestçe Y yerine ikame edebileceğini ve bunun tersine sınıfı böyle bir ikame sorununa neden olacak şekilde kullanmaktan kaçınması gerektiğini ImmutablePointbelirtiyorsa X.equals(Y), olası bir LSP ihlali olarak değerlendiririm . Tür equals, tüm örneklerin farklı olarak karşılaştırılacağı şekilde yasal olarak tanımlanabilir , ancak bu tür davranışlar muhtemelen yararlılığını sınırlar.
supercat

2

(Bu, C # açısından yazılmıştır, bu nedenle kontrol edilen istisna yoktur.)

LSP hakkındaki Wikipedia makalesine göre, LSP'nin koşullarından biri:

Alt tipin yöntemleriyle atılan istisnaların alt türleri olduğu durumlar dışında, alt tipin yöntemleriyle yeni bir istisna atılmamalıdır.

Bunu nasıl anlamalısınız? Supertype yöntemleri soyut olduğunda “supertype yöntemleri tarafından atılan istisnalar” tam olarak nedir? Bence bunlar , süpertip yöntemlerle atılabilecek istisnalar olarak belgelenen istisnalar.

Bunun anlamı şudur: OrderState.Ship()InvalidOperationExceptionbu işlemler mevcut durum tarafından desteklenmiyorsa atar” gibi bir belge olarak belgelenirse , bu tasarımın LSP'yi bozmadığını düşünüyorum. Öte yandan, süper tip yöntemler bu şekilde belgelenmezse, LSP ihlal edilir.

Ancak bu, bunun iyi bir tasarım olduğu anlamına gelmez, normal kontrol akışı için çok yakın görünen istisnalar kullanmamalısınız. Ayrıca, gerçek bir uygulamada, muhtemelen bir işlemin gerçekleştirilmeden önce gerçekleştirilip gerçekleştirilemeyeceğini bilmek istersiniz , örneğin kullanıcı arayüzündeki “Gönder” düğmesini devre dışı bırakmak.


Aslında bu beni düşünmeye sevk eden nokta. Alt sınıflar üst öğede tanımlı olmayan istisnalar atarsa, bu LSP ihlali olmalıdır.
Songo

İstisnaların kontrol edilmediği bir dilde, onları atmanın LSP'nin ihlali olmadığını düşünüyorum. Bu sadece bir dilin kendisidir, herhangi bir zamanda herhangi bir istisna atılabilir. Yani herhangi bir istemci kodu buna hazır olmalıdır.
Zapadlo
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.