Geçersiz kullanıcı girişini nasıl ele almalıyım?


12

Bir süredir bu konuyu düşünüyorum ve diğer geliştiricilerin görüşlerini almak isterdim.

Çok savunmacı bir programlama tarzım var. Benim tipik blok veya yöntem şöyle görünür:

T foo(par1, par2, par3, ...)
{
    // Check that all parameters are correct, return undefined (null)
    // or throw exception if this is not the case.

    // Compute and (possibly) return result.
}

Ayrıca, hesaplama sırasında, tüm işaretçileri silmeden önce kontrol ediyorum. Benim fikrim, eğer bir hata varsa ve bir yerde NULL işaretçisi görünmesi gerekiyorsa, programım bu güzel işlemeli ve sadece hesaplamaya devam etmeyi reddetmelidir. Tabii ki günlükte bir hata mesajı veya başka bir mekanizma ile sorunu bildirebilir.

Daha soyut bir şekilde ifade etmek gerekirse, yaklaşımım

if all input is OK --> compute result
else               --> do not compute result, notify problem

Diğer geliştiriciler, aralarında bazı meslektaşlarım başka bir strateji kullanıyorlar. Örneğin, işaretçileri kontrol etmezler. Bir kod parçasına doğru girdi verilmesi gerektiğini ve girdinin yanlış olması durumunda olanlardan sorumlu olmaması gerektiğini varsayarlar. Ayrıca, bir NULL işaretçi istisnası programı çökerse, hata sırasında test sırasında daha kolay bir hata bulunacak ve düzeltilme şansı daha fazla olacaktır.

Buna cevabım normalde: ancak test sırasında hata bulunmazsa ve ürün müşteri tarafından zaten kullanılıyorsa görünürse ne olur? Hatanın kendini göstermesi için tercih edilen bir yol nedir? Belirli bir eylemi gerçekleştirmeyen, ancak yine de çalışmaya devam edebilen bir program mı yoksa çöküp yeniden başlatılması gereken bir program mı olmalı?

Özetleme

Yanlış girdiyi ele almak için iki yaklaşımdan hangisini önerirsiniz?

Inconsistent input --> no action + notification

veya

Inconsistent input --> undefined behaviour or crash

Düzenle

Cevap ve önerileriniz için teşekkürler. Ben de sözleşme ile tasarım hayranıyım. Ama benim yöntemleri (belki de kendim) çağıran kodu yazdı kişiye güven bile, yine de yanlış giriş yol açan, hatalar olabilir. Bu yüzden yaklaşımım asla bir yöntemin doğru girdiden geçtiğini varsaymamaktır.

Ayrıca, sorunu yakalamak ve bunu bildirmek için bir mekanizma kullanırdım. Bir geliştirme sisteminde, kullanıcıyı bilgilendirmek için bir iletişim kutusu açılır. Bir üretim sisteminde sadece günlüğe bazı bilgiler yazacaktır. Fazladan kontrollerin performans sorunlarına yol açabileceğini düşünmüyorum. İddiaların yeterli olup olmadığından emin değilim, eğer bir üretim sisteminde kapatılmışlarsa: belki üretim sırasında test sırasında meydana gelmemiş bir durum ortaya çıkacaktır.

Her neyse, birçok kişinin zıt yaklaşımı izlemesine gerçekten şaşırdım: uygulamanın "bilerek" çökmesine izin verdiler, çünkü bunun test sırasında hata bulmayı kolaylaştıracağını savunuyorlar.


Daima savunmacı bir şekilde kodlayın. Sonunda, performans nedeniyle, bazı testleri serbest bırakma modunda devre dışı bırakmak için bir anahtar koyabilirsiniz.
deadalnix

Bugün eksik bir NULL işaretçi denetimi ile ilgili bir hata düzeltildi. Uygulama oturumu kapatılırken bazı nesneler oluşturuldu ve yapıcı artık orada olmayan başka bir nesneye erişmek için bir alıcı kullandı. Nesnenin o noktada yaratılması amaçlanmamıştır. Başka bir hata nedeniyle oluşturuldu: çıkış sırasında bazı zamanlayıcı durdurulmadı -> bir sinyal gönderildi -> alıcı bir nesne oluşturmaya çalıştı -> yapıcı sorgulandı ve başka bir nesne kullandı -> NULL işaretçisi -> kilitlenme ). Gerçekten böyle kötü bir durum benim uygulama çökmesini istemem.
Giorgio

1
Onarım Kuralı: Başarısız olmanız gerektiğinde, gürültülü ve en kısa zamanda başarısız olun.
deadalnix

"Onarım Kuralı: Başarısız olmanız gerektiğinde, gürültülü ve en kısa zamanda başarısız olun.": Sanırım tüm bu Windows BSOD'leri bu kuralın bir uygulamasıdır. :-)
Giorgio

Yanıtlar:


8

Doğru anladınız. Paranoyak olun. Kendi kodunuz olsa bile diğer koda güvenmeyin. Bir şeyleri unutuyorsunuz, değişiklikler yapıyorsunuz, kod gelişiyor. Dış koda güvenmeyin.

Yukarıda iyi bir noktaya değinildi: girişler geçersizse ancak program çökmezse ne olur? Sonra veritabanında çöp ve satır aşağı hata alırsınız.

Ne zaman bir numara istedi (örneğin dolar veya birim sayısı olarak fiyat) "1e9" girmek ve kodun ne yaptığını görmek istiyorum. Olabilir.

Kırk yıl önce, Bilgisayar Bilimleri alanında lisans derecemi UCBerkeley'den aldığımızda, iyi bir programın% 50 hata işleme olduğu söylendi. Paranoyak olun.


Evet, IMHO, paranoyak olmanın bir sorun değil, bir özellik olduğu birkaç durumdan biri.
Giorgio

"Ya girişler geçersiz ama program çökmezse? O zaman veritabanında çöp ve satır aşağı hataları alırsınız.": Program çökmesini yerine işlemi gerçekleştirmek ve tanımsız bir sonuç döndürmeyi reddedebilir. Tanımsız, hesaplama yoluyla yayılacak ve hiçbir çöp üretilmeyecektir. Ancak programın bunu başarması için çökmesi gerekmez.
Giorgio

Evet, ama - benim açımdan programın geçersiz girişi ALINMASI ve onunla başa çıkması gerekiyor. Giriş kontrol edilmezse, sisteme girecek ve daha sonra kötü şeyler gelecektir. Çökme bile bundan daha iyidir!
Andy Canfield

Size tamamen katılıyorum: tipik yöntemim veya fonksiyonum, giriş verilerinin doğru olduğundan emin olmak için bir dizi kontrol ile başlar.
Giorgio

Bugün yine "her şeyi kontrol et, hiçbir şeye güvenme" stratejisinin genellikle iyi bir fikir olduğunu doğruladım. Bir meslektaşım, eksik bir kontrol nedeniyle NULL işaretçisi istisnası vardı. Bu bağlamda, bazı veriler yüklenmemiş olduğu için bir NULL işaretçisine sahip olmanın doğru olduğu ortaya çıktı ve işaretçiyi kontrol etmek ve NULL olduğunda hiçbir şey yapmak doğru değildi. :-)
Giorgio

7

Zaten doğru fikrin var

Yanlış girdiyi ele almak için iki yaklaşımdan hangisini önerirsiniz?

Tutarsız girdi -> işlem yok + bildirim

ya da daha iyisi

Tutarsız girdi -> uygun şekilde ele alınan eylem

Programlamaya gerçekten bir çerez kesici yaklaşımı alamazsınız (yapabilirsiniz), ancak bilinçli seçimden ziyade işleri alışkanlıktan çıkaran formülik bir tasarımla sonuçlanırsınız.

Pragmatizm ile temper dogmatizm.

Steve McConnell en iyisini söyledi

Steve McConnell , savunma programlama üzerine kitabı ( Kod Tamamlandı ) yazdı ve bu, girişlerinizi her zaman doğrulamanız gerektiğini önerdiği yöntemlerden biriydi.

Steve'in bundan bahsettiğini hatırlamıyorum, ancak bunu özel olmayan yöntemler ve işlevler için ve sadece gerekli görülen yerlerde yapmayı düşünmelisiniz .


2
Herkese açık olmak yerine, özel olmayan tüm yöntemlerin, erişim kısıtlaması kavramını koruyan, paylaşan veya hiç olmayan kavramları (her şey herkese açık, örtülü olarak) koruyan dilleri savunmacı bir şekilde kapsamayı öneriyorum.
JustinC

3

Burada, özellikle dili, kod türünü ve kodun girebileceği ürün türünü belirtmeden "doğru" bir cevap yoktur. Düşünmek:

  • Dil önemlidir. Objective-C'de nil'e mesaj göndermek genellikle iyidir; hiçbir şey olmuyor, ama program da çökmüyor. Java'nın açık işaretçileri yoktur, bu yüzden sıfır işaretçiler burada büyük bir endişe değildir. C'de biraz daha dikkatli olmanız gerekir.

  • Paranoyak olmak mantıksız, haksız şüphe veya güvensizliktir. Bu muhtemelen yazılım için insanlar için olduğundan daha iyi değildir.

  • Endişe düzeyiniz, koddaki risk seviyesi ve ortaya çıkan sorunları tespit etmenin olası zorluğu ile orantılı olmalıdır. En kötü durumda ne olur? Kullanıcı programı yeniden başlatır ve kaldığı yerden devam eder mi? Şirket milyonlarca dolar kaybeder mi?

  • Her zaman bozuk girdiyi tanımlayamazsınız. İşaretçilerinizi dini olarak nil ile karşılaştırabilirsiniz, ancak bu , neredeyse tümü kötü olan 2 ^ 32 olası değerden yalnızca birini yakalar .

  • Hatalarla başa çıkmak için birçok farklı mekanizma vardır. Yine, bir dereceye kadar dile bağlıdır. Onay makroları, koşullu ifadeler, birim testleri, istisna yönetimi, dikkatli tasarım ve diğer teknikleri kullanabilirsiniz. Hiçbiri kusursuz değildir ve hiçbiri her durum için uygun değildir.

Yani, çoğunlukla sorumluluk koymak istediğiniz yere kaynar. Başkaları tarafından kullanılmak üzere bir kitaplık yazıyorsanız, muhtemelen aldığınız girdiler hakkında mümkün olduğunca dikkatli olmak ve mümkün olduğunda yararlı hatalar yaymak için elinizden gelenin en iyisini yapmak istersiniz. Kendi özel işlevlerinizde ve yöntemlerinizde, aptalca hataları yakalamak için ekleri kullanabilirsiniz, ancak aksi takdirde çöpü geçmemek için arayana (bu sensin) sorumluluk koyabilirsiniz.


+1 - İyi cevap. Temel kaygım, yanlış bir girdinin bir üretim sisteminde ortaya çıkan bir soruna neden olabileceğidir (bu konuda bir şey yapmak için çok geç olduğunda). Tabii ki, bence böyle bir sorunun kullanıcıya verebileceği hasara bağlı olduğunu söylemekte haklısınız.
Giorgio

Dil büyük bir rol oynar. PHP'de yöntem kodunuzun yarısı, değişkenin ne tür olduğunu kontrol eder ve uygun eylemi gerçekleştirir. Java'da yöntem bir int kabul ederse, başka bir şey iletemezsiniz, böylece yönteminiz daha net olur.
chap

1

Kesinlikle atılan bir istisna gibi bir bildirim olmalıdır. Yazdıklarının kodunu yanlış kullanabilecek (yapması amaçlanmayan bir şey için kullanmaya çalışarak) girişlerinin geçersiz olduğu veya hatalara neden olabileceği diğer kodlayıcılara yön veriyor. Bu, hataları izlemede çok yararlıdır, ancak yalnızca null değerini döndürürseniz, sonucu kullanmaya ve farklı koddan istisna elde etmeye çalışana kadar kodları devam eder.

Kodunuz, söz konusu kodun kapsamı dışında olan başka bir koda (belki de başarısız bir veritabanı güncellemesi) yapılan bir çağrı sırasında bir hatayla karşılaşırsa, bu kod üzerinde gerçekten hiçbir kontrole sahip olmazsınız ve tek başvurunuz neyi açıklayan bir istisna atmaktır biliyorsunuz (sadece size söylediğiniz kodla ne söylendi). Belirli girişlerin kaçınılmaz olarak böyle bir sonuca yol açacağını biliyorsanız, kodunuzu yürütmeye zahmet edemez ve hangi girişin geçerli olmadığını ve nedenini belirten bir istisna atamazsınız.

Son kullanıcıyla ilgili bir notta, herkesin anlayabilmesi için açıklayıcı ancak basit bir şey döndürmek en iyisidir. Müşteriniz "program çöktü, düzeltin" diyor ve diyorsa, neyin yanlış gittiğini ve nedenini izleyerek ve sorunu yeniden oluşturabileceğinizi umarak ellerinizde çok fazla işiniz var. İstisnaların uygun şekilde kullanılması yalnızca bir çökmeyi önlemekle kalmaz, aynı zamanda değerli bilgiler de sağlar. Bir istemciden "Program bana bir hata veriyor." XYZ, yöntem M için geçerli bir girdi değil, çünkü Z çok büyük olduğu için "veya bunun gibi bir şey bilmiyor olsalar bile, böyle bir şey diyor ki, tam olarak nereye bakacağını bil. Ayrıca, şirketinizin / şirketinizin iş uygulamalarına bağlı olarak, bu sorunları gidermeniz bile olmayabilir, bu yüzden onlara iyi bir harita bırakmak en iyisidir.

Cevabımın kısa versiyonu ilk seçeneğinizin en iyisi olması.

Inconsistent input -> no action + notify caller

1

Programlamada bir üniversite dersinden geçerken de aynı sorunla mücadele ettim. Paranoyak tarafa doğru eğildim ve her şeyi kontrol etme eğilimindeydim ama bunun yanlış yönlendirilmiş davranış olduğu söylendi.

Bize "Sözleşme ile tasarım" öğretiliyordu. Vurgu, yorumlarda ve tasarım belgelerinde ön koşulların, değişmezlerin ve post-koşulların belirtilmesidir. Kodun bir parçasını uygulayan kişi olarak, yazılım mimarına güvenmeli ve ön koşulları (hangi yöntemlerimin işleyebilmesi gereken girdiler ve hangi girdiler gönderilmeyecek) içerecek spesifikasyonları izleyerek güçlendirmeliyim. . Her yöntem çağrısında aşırı kontrol yapılması şişkinliğe neden olur.

Derleme yinelemeleri sırasında programın doğruluğunu (ön koşulların, değişmezlerin, posta koşullarının doğrulanması) doğrulanması için iddialar kullanılmalıdır. Bu durumda üretim derlemesinde iddialar geri çevrilecektir.


0

"İddiaları" kullanmak, diğer geliştiricilere bunu yanlış yaptıklarını, yalnızca "özel" yöntemlerle bildirmenin yoludur . Bunları etkinleştirmek / devre dışı bırakmak, derleme zamanında eklemek / kaldırmak için yalnızca bir işarettir ve bu nedenle, üretim kodundan iddiaları kaldırmak kolaydır. Olmadığını bilmek harika bir araç da vardır sen nasılsa kendi yöntemlerde yanlış bunu yapıyor.

Genel / korumalı yöntemlerde giriş parametrelerini doğrulamak için, savunmacı bir şekilde çalışmayı ve parametreleri kontrol etmeyi ve InvalidArgumentException veya benzerlerini atmayı tercih ederim. İşte bu yüzden buradalar. Ayrıca bir API yazıp yazmadığınıza da bağlıdır. Bir API ise ve hatta kapalı kaynaksa daha da fazlası, geliştiricilerin neyin yanlış gittiğini tam olarak bilmesi için her şeyi daha iyi doğrulayın. Aksi takdirde, kaynak diğer geliştiriciler tarafından kullanılabilirse, siyah / beyaz değildir. Sadece seçimlerinizle tutarlı olun.

Düzenleme: sadece eklemek için örneğin Oracle JDK, onlar asla "null" kontrol ve kod çökmesine izin göreceksiniz. Zaten bir NullPointerException kuracağından, null olup olmadığını kontrol etmek ve açık bir istisna atmak neden zahmete giriyor. Sanırım mantıklı.


Java'da bir boş işaretçi istisnası alırsınız. C ++ 'da boş bir işaretçi uygulamayı kilitler. Belki başka örnekler de vardır: sıfıra bölme, dizin aralık dışında, vb.
Giorgio
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.