kod sözleşmeleri / iddiaları: yinelenen kontrollerle ne olacak?


10

Ben büyük bir yazma hayranları, sözleşmeler ya da kullandığım dilde mevcut her türlü çekler hayranıyım. Beni biraz rahatsız eden bir şey, tekrarlanan kontrollerle uğraşmak için ortak uygulamanın ne olduğundan emin değilim.

Örnek durum: İlk önce aşağıdaki işlevi yazıyorum

void DoSomething( object obj )
{
  Contract.Requires<ArgumentNullException>( obj != null );
  //code using obj
}

birkaç saat sonra birincisini çağıran başka bir işlev yazıyorum. Her şey hala bellekte taze olduğundan, DoSomethingzaten boş bir nesneyi kontrol edeceğini bildiğim için sözleşmeyi çoğaltmamaya karar verdim :

void DoSomethingElse( object obj )
{
  //no Requires here: DoSomething will do that already
  DoSomething( obj );
  //code using obj
}

Bariz sorun: DoSomethingElseşimdi DoSomethingobj 'in null olmadığını doğrulamaya bağlı . Bu nedenle, DoSomethingartık kontrol etmemeye karar vermeli veya başka bir işlev kullanmaya karar verirsem obj artık kontrol edilmeyebilir. Sonuçta bu uygulamayı yazmama neden oluyor:

void DoSomethingElse( object obj )
{
  Contract.Requires<ArgumentNullException>( obj != null );
  DoSomething( obj );
  //code using obj
}

Her zaman güvenli, endişelenmeyin, ancak durum büyüdükçe aynı nesne birkaç kez kontrol edilebilir ve bu bir çoğaltma biçimidir ve hepimiz bunun çok iyi olmadığını biliyoruz.

Bu gibi durumlar için en yaygın uygulama nedir?


3
ArgumentBullException? Bu yeni bir tane :)
CVn

lol @ yazma becerilerim ... Düzenleyeceğim.
stijn

Yanıtlar:


13

Şahsen ben null olursa başarısız olacak herhangi bir fonksiyonda null olup olmadığını kontrol eder ve olmaz herhangi bir fonksiyonda değil.

Yukarıdaki örneğinizde, eğer doSomethingElse () nesnesinin itirazını kaldırması gerekmiyorsa, obj için null değerini kontrol etmem.

DoSomething () yöntemi dereference obj yaparsa null olup olmadığını kontrol etmelidir.

Eğer her iki fonksiyon da referans gerektirmiyorsa, her ikisi de kontrol etmelidir. Dolayısıyla, DoSomethingElse dereferences obj ise null değerini kontrol etmelidir, ancak DoSomething yine başka bir yoldan çağrılabileceği için null olup olmadığını da kontrol etmelidir.

Bu şekilde kodu oldukça temiz bırakabilir ve yine de kontrollerin doğru yerde olduğunu garanti edebilirsiniz.


1
Tamamen katılıyorum. Her yöntemin önkoşulları kendi başlarına durmalıdır. Önkoşul DoSomething()artık gerekli olmayacak şekilde yeniden yazdığınızı (bu özel durumda olması muhtemel değildir, ancak farklı bir durumda olabilir) ve önkoşul denetimini kaldırın. Şimdi eksik önkoşul nedeniyle görünüşte tamamen ilgisiz bazı yöntemler bozuldu. Her gün birkaç satır kod kaydetme arzusundan tuhaf hatalar üzerine netlik için biraz kod çoğaltması yapacağım.
CVn

2

Harika! .NET için Kod Sözleşmeleri hakkında bilgi sahibi olduğunuzu görüyorum . Kod Sözleşmeleri, statik denetleyicinin en iyi örnek olduğu ortalama iddialarınızdan çok daha ileri gider . Visual Studio Premium veya daha üst sürümü yüklü değilse bu sizin için geçerli olmayabilir, ancak Kod Sözleşmelerini kullanacaksanız arkasındaki niyeti anlamak önemlidir.

Bir işleve bir sözleşme uyguladığınızda, bu kelimenin tam anlamıyla bir sözleşmedir . Bu işlev sözleşmeye göre davranmayı garanti eder ve sadece sözleşmede tanımlandığı şekilde kullanılması garanti edilir.

DoSomethingElse()Belirttiğiniz örnekte, işlev DoSomething()null iletilebileceği için sözleşmede belirtilen şekilde geçerli değil ve statik denetleyici bu sorunu gösterecektir. Bunu çözmenin yolu, aynı sözleşmeyi eklemektir DoSomethingElse().

Bu, çoğaltma olacağı anlamına gelir, ancak işlevselliği iki işlev arasında göstermeyi seçtiğiniz için bu çoğaltma gereklidir . Bu işlevler, özel olsa da, sınıfınızdaki farklı yerlerden de çağrılabilir, bu nedenle herhangi bir çağrıdan argümanın asla boş kalmayacağını garanti etmenin tek yolu sözleşmeleri çoğaltmaktır.

Bu, davranışı ilk başta neden iki işlevde bölündüğünüzü yeniden düşünmenizi sağlayacaktır. Her zaman benim düşüncem ( halk inancının aksine ) sadece tek bir yerden çağrılan işlevleri bölmemeniz gerekti . Sözleşmeleri uygulayarak kapsüllenmeyi ortaya çıkarmak bu daha da belirginleşir. Görünüşe göre davam için ekstra bir argüman buldum! Teşekkür ederim! :)


Son paragrafınızla ilgili olarak: gerçek kodda her iki işlev de iki farklı sınıfın üyesidir, bu yüzden bölünürler. Bunun dışında birçok kez şu durumdaydım: uzun bir işlev yazın, bölmemeye karar verin. Daha sonra mantığın bir kısmının başka bir yerde kopyalandığını anlayın, bu yüzden yine de bölün. Ya da bir yıl sonra tekrar okuyun ve okunamaz bir şekilde bulun, bu yüzden yine de bölün. Veya hata ayıklama sırasında: split fonksiyonları = F10 tuşuna daha az vurma. Daha fazla neden var, bu yüzden bazen çok aşırı olabileceği anlamına gelse de kişisel olarak bölünmeyi tercih ediyorum.
stijn

(1) "Daha sonra mantığın bir kısmının başka bir yerde kopyalandığını anlayın" . Bu yüzden her zaman "bir API doğru geliştirmek" sadece fonksiyonları bölmekten daha önemli buluyorum. Sadece mevcut sınıf içinde değil, sürekli olarak yeniden kullanmayı düşünün. (2) "Ya da bir yıl sonra tekrar okuyup okunamaz bul" Fonksiyonların isimleri olduğundan bu daha mı iyi? Blogumdaki bir yorumcunun "kod paragrafları" olarak tanımladığı şeyi kullanırsanız daha fazla okunabilirlik elde edersiniz . (3) "bölünmüş işlevler = F10 tuşuna daha az vurma" ... Nedenini anlamıyorum.
Steven Jeuris

(1) kararlaştırılmış (2) okunabilirlik kişisel tercihtir, bu yüzden benim için tartışılan bir şey değildir. (3) 20 satırlık bir fonksiyondan geçmek F10'a 20 kez vurmayı gerektirir. Bölünmüş bir işlevde bu satırların 10'una sahip bir işlevden geçmek, bana sadece F10'a 11 kez vurmak zorunda kalma seçeneği sunar. Evet, ilk durumda kesme noktaları koyabilir veya 'imlece atla' seçeneğini seçebilirim, ancak bu yine de ikinci durumdan daha fazla çaba gerektirir.
stijn

@stijn: (2) kabul etti; p (3) açıkladığın için teşekkürler!
Steven Jeuris
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.