Gereksiz durum en iyi uygulamalara karşı kontrol ediliyor mu?


16

Son üç yıldır yazılım geliştiriyorum, ancak kısa bir süre önce iyi uygulamalardan ne kadar cahil olduğum konusunda uyandım. Bu, hayatımı daha iyiye çeviren Temiz Kod kitabını okumaya başlamamı sağladı, ancak programlarımı yazmak için en iyi yaklaşımlardan bazılarını anlamak için mücadele ediyorum.

Ben bir Python program var ...

  1. required=Trueher ikisi de dosya adı olan iki bağımsız değişkeni zorlamak için argparse komutunu kullanın. birincisi girdi dosyasının adı, ikincisi çıktı dosyasının adı
  2. readFromInputFileönce bir girdi dosyası adının girildiğini kontrol eden bir işleve sahip olmak
  3. writeToOutputFileönce bir çıktı dosyası adının girildiğini kontrol eden bir işleve sahip olmak

Programım # 2 ve # 3 kontrolünün gereksiz olduğunu ve kaldırılması gerektiğine inanmam için yeterince küçük, böylece her iki işlevi de gereksiz bir ifkoşuldan kurtarıyor . Ancak, "çift denetleme tamam" olduğuna ve işlevlerin bağımsız değişkenlerin ayrıştırılmasının olmadığı farklı bir konumdan çağrılabileceği bir programda doğru çözüm olabileceğine inanmaya yönlendirildim.

(Ayrıca, okuma veya yazma başarısız olursa, try excepther işlevde uygun bir hata iletisi oluşturmak için bir tane var.)

Sorum şu: Tüm gereksiz durum kontrolünden kaçınmak en iyisi mi? Bir programın mantığı, kontrollerin yalnızca bir kez yapılması gerektiği kadar sağlam mı olmalı? Bunu veya bunun tersini gösteren iyi örnekler var mı?

EDIT: cevaplar için hepinize teşekkür ederim! Her birinden bir şey öğrendim. Bu kadar çok perspektif görmek, bu soruna nasıl yaklaşacağımı ve gereksinimlerime göre bir çözümü nasıl belirleyeceğimi daha iyi anlamamı sağlıyor. Teşekkür ederim!


Sorunuzun yoğun şekilde genelleştirilmiş bir sürümü: softwareengineering.stackexchange.com/questions/19549/… . Ben daha büyük bir odak var çünkü yinelenen olduğunu söyleyemem, ama belki de yardımcı olur.
Doc Brown

Yanıtlar:


15

Sorduğunuz şeye "sağlamlık" denir ve doğru ya da yanlış cevap yoktur. Programın büyüklüğüne ve karmaşıklığına, programda çalışan insanların sayısına ve başarısızlıkları tespit etmenin önemine bağlıdır.

Yalnız ve sadece kendiniz için yazdığınız küçük programlarda, sağlamlık genellikle bir ekip tarafından yazılan birden fazla bileşenden oluşan karmaşık bir program yazacağınızdan daha küçük bir endişedir. Bu tür sistemlerde, bileşenler arasında genel API'ler biçiminde sınırlar vardır ve her sınırda, "programın mantığı bu denetimler gereksiz olacak kadar sağlam olsa bile, giriş parametrelerini doğrulamak genellikle iyi bir fikirdir ". Bu, hataların algılanmasını oldukça kolaylaştırır ve hata ayıklama sürelerini daha küçük tutmaya yardımcı olur.

Sizin durumunuzda, programınız için ne tür bir yaşam döngüsü beklediğinize kendiniz karar vermelisiniz. Yıllarca kullanılmasını ve sürdürülmesini beklediğiniz bir program mı? Daha sonra gereksiz bir kontrol eklemek muhtemelen daha iyidir, çünkü kodunuzun gelecekte yeniden düzenlenmesi olası değildir ve readve writeişlevleriniz farklı bir bağlamda kullanılabilir.

Yoksa sadece öğrenme veya eğlence amaçlı küçük bir program mı? O zaman bu çift kontroller gerekli olmayacak.

"Temiz Kod" bağlamında, bir çift kontrolün KURU ilkesini ihlal edip etmediği sorabilir. Aslında, bazen en azından bir dereceye kadar: giriş validasyonu, bir programın iş mantığının bir parçası olarak yorumlanabilir ve bunun iki yerde bulunması DRY'nin ihlali nedeniyle olağan bakım sorunlarına yol açabilir. Sağlamlık ve DRY genellikle bir ödünleşmedir - sağlamlık kodda fazlalık gerektirirken, DRY fazlalığı en aza indirmeye çalışır. Artan program karmaşıklığı ile sağlamlık, doğrulamada KURU olmaktan daha önemli hale gelir.

Son olarak, davanızda bunun ne anlama geldiğini bir örnek vereyim. Gereksinimlerinizin benzer bir şeye değiştiğini varsayalım

  • program aynı zamanda bir argüman ile çalışır, girdi dosya adı, çıktı dosyası adı verilmemişse, sonek değiştirilerek otomatik olarak girdi dosya adından oluşturulur.

Bu, çifte doğrulamanızı iki yerde değiştirmeniz gerektiğini gösteriyor mu? Muhtemelen hayır, böyle bir gereksinim çağrıldığında bir değişikliğe neden olur argparse, ancak hiçbir değişiklik olmaz writeToOutputFile: bu işlev yine de bir dosya adı gerektirecektir. Yani sizin durumunuzda, giriş doğrulamasını iki kez yapmak için oy vereceğim, değiştirmek için iki yer olması nedeniyle bakım problemleri alma riski IMHO, çok az kontrolün neden olduğu maskelenmiş hatalar nedeniyle bakım problemleri alma riskinden çok daha düşük.


"... genel API'ler biçimindeki bileşenler arasındaki sınırlar ..." "sınıfların sınırları aştığını" gözlemliyorum. Yani gerekli olan bir sınıftır; tutarlı bir iş alanı sınıfı. Bu OP'den her yerde "basit, bu yüzden bir sınıfa gerek yok" ilkesinin iş başında olduğu sonucuna varıyorum. "Birincil nesne" yi saran basit bir sınıf olabilir, "bir dosyanın bir adı olması gerekir" gibi iş kurallarını zorunlu kılan, sadece mevcut kodu KURU değil, aynı zamanda gelecekte KURU tutar.
radarbob

@ radarbob: yazdıklarım OOP veya sınıf biçimindeki bileşenlerle sınırlı değil. Bu aynı zamanda, nesne yönelimli olsun olmasın, herkese açık bir API'ye sahip rastgele kütüphaneler için de geçerlidir.
Doc Brown

5

Artıklık günah değildir. Gereksiz fazlalık.

  1. Eğer readFromInputFile()ve writeToOutputFile()herhangi bir kamu fonksiyonları (ve Python isimleri iki alt çizgi ile başlamadı çünkü oldukları adlandırma ile) daha sonra fonksiyonlar birgün tamamen argparse kaçınılması biri tarafından kullanılabilir. Bu, argümanları bıraktıklarında özel argparse hata mesajınızı görmedikleri anlamına gelir.

  2. Parametrelerin kendileri readFromInputFile()olup writeToOutputFile()olmadığını kontrol ederseniz , dosya adlarına olan ihtiyacı açıklayan özel bir hata mesajı tekrar gösterebilirsiniz.

  3. Eğer readFromInputFile()ve writeToOutputFile()kendileri parametreler için kontrol etmiyoruz, hiçbir özel hata mesajı gösterilir. Kullanıcı, ortaya çıkan istisnayı kendi başına çözmelidir.

Her şey 3'e iner. Argparse'den kaçınarak bu işlevleri kullanan bir kod yazın ve hata mesajını üretin. Bu işlevlerin içine hiç bakmadığınızı ve sadece kullanmak için yeterli anlayış sağlamak için isimlerine güvendiğinizi düşünün. Tek bildiğiniz şey istisna ile karıştırılmanın bir yolu var mı? Özelleştirilmiş bir hata mesajına ihtiyaç var mı?

Beyninizin bu işlevlerin iç kısımlarını hatırlayan kısmını kapatmak zordur. Öyle ki, bazıları kullanılan koddan önce using kodunu yazmanızı önerir. Bu şekilde, dışarıdan nasıl bir şey göründüğünü bilerek soruna gelirsiniz. Bunu yapmak için TDD yapmak zorunda değilsiniz, ancak TDD yaparsanız, ilk önce dışarıdan geliyor olacaksınız.


4

Eğer yöntemlerini yapmak hangi ölçüde tek başına ve yeniden kullanılabilir ölçünüz iyi bir şeydir. Bu, yöntemlerin kabul ettikleri şeyde affedici olması ve iyi tanımlanmış çıktılara (geri döndüklerinde kesin) sahip olması gerektiği anlamına gelir. Bu aynı zamanda kendilerine geçirilen her şeyi zarif bir şekilde ele alabilmeleri ve girdinin niteliği, kalitesi, zamanlaması vb. Hakkında herhangi bir varsayımda bulunmamaları gerektiği anlamına gelir .

Bir programcı, geçirilenler hakkında varsayımlar yapan yöntemler yazma alışkanlığı içindeyse, "bu bozuksa, endişelenecek daha büyük şeylere sahibiz" veya "X parametresi Y değerine sahip olamaz, çünkü geri kalanı kod bunu önler ", o zaman birdenbire artık bağımsız, ayrılmış bileşenlere sahip değilsiniz. Bileşenleriniz aslında daha geniş sisteme bağımlıdır. Bu bir tür ince sıkı bağlantıdır ve sistem karmaşıklığı arttıkça toplam sahip olma maliyetini katlanarak arttırır.

Bunun, aynı bilgileri birden çok kez doğruladığınız anlamına gelebileceğini unutmayın. Ama sorun değil. Her bileşen kendi doğrulamasından kendi yolundan sorumludur . Bu DRY'nin ihlali değildir, çünkü onaylamalar birbirinden bağımsız bileşenler tarafından yapılmıştır ve birindeki doğrulamada bir değişiklik tam olarak diğerinde çoğaltılmak zorunda değildir. Burada artıklık yok. X, girdilerini kendi ihtiyaçları için kontrol etme ve bir kısmını Y'ye aktarma sorumluluğuna sahiptir. Y, ihtiyaçları için kendi girdilerini kontrol etme sorumluluğuna sahiptir .


1

Bir fonksiyonunuz olduğunu varsayalım (C cinsinden)

void readInputFile (const char* path);

Ve yol hakkında herhangi bir belge bulamazsınız. Ve sonra uygulamaya bakıyorsunuz ve diyor ki

void readInputFile (const char* path)
{
    assert (path != NULL && strlen (path) > 0);

Bu sadece işlevin girişini test etmekle kalmaz, aynı zamanda kullanıcıya işlevin yolun NULL olmasına veya boş bir dize olmasına izin verilmediğini de söyler.


0

Genel olarak, çift kontrol her zaman iyi veya kötü değildir. Söz birçok yönü her zaman vardır sizin özel durumda olursa olsun bağımlı olduğu. Senin durumunda:

  • Program ne kadar büyük? Ne kadar küçükse, arayan kişinin doğru şeyi yaptığı o kadar açıktır. Programınız büyüdükçe, her rutinin ön koşullarının ve son koşullarının tam olarak ne olduğunu belirtmek daha önemli hale gelir.
  • argümanlar argparsemodül tarafından zaten kontrol edilir . Bir kütüphane kullanmak ve daha sonra işini kendiniz yapmak genellikle kötü bir fikirdir. Neden kütüphaneyi kullanıyorsunuz?
  • Yöntemin, arayanın bağımsız değişkenleri kontrol etmediği bir bağlamda yeniden kullanılması olasılığı nedir? Ne kadar olası olursa, argümanları doğrulamak o kadar önemlidir.
  • Bir argüman ne olur mu kayboluyor? Bir giriş dosyası bulamamanız büyük olasılıkla doğrudan işlemeyi durduracaktır. Bu muhtemelen düzeltilmesi kolay bariz bir arıza modudur. Sinsi hatalar, programın fark etmeden neşeyle çalışmaya devam ettiği ve yanlış sonuçlar ürettiği hatalardır .

0

Çift çekleriniz nadiren kullanıldığı yerlerde gibi görünüyor. Bu kontroller programınızı daha sağlam hale getiriyor:

Bir çek çok fazla acıtmaz, çok daha azı olabilir.

Ancak, sık sık tekrarlanan bir döngü içinde kontrol ediyorsanız, kontrolün kendisi, çoğu zaman kontrolün ardından gelenlere kıyasla pahalı olmasa bile, artıklığı kaldırmayı düşünmelisiniz.


Ve zaten sahip olduğunuz için, bir döngüde veya başka bir şeyde değilse, kaldırma çabalarına değmez.
StarWeaver

0

Belki bakış açınızı değiştirebilirsiniz:

Bir şeyler ters giderse, sonuç nedir? Uygulamanıza / kullanıcıya zarar verir mi?

Elbette, az ya da çok kontrolün daha iyi veya daha kötü olup olmadığını her zaman tartışabilirsiniz, ancak bu oldukça skolastik bir sorudur. Ve gerçek dünyayla uğraştığın için yazılımlarıyla , gerçek dünya sonuçları var.

Verdiğiniz bağlamdan:

  • bir girdi dosyası A
  • bir çıktı dosyası B

Sana gelen dönüşüm yaptığını varsayalım A için B . Eğer A ve B küçük ve dönüşüm küçük, sonuçları nelerdir?

1) Nereden okunacağını belirtmeyi unuttun: O zaman sonuç hiçbir şey değil . Ve yürütme süresi beklenenden daha kısa olacak. Sonuca bakıyorsunuz - ya da daha iyisi: eksik bir sonuç arayın, komutu yanlış bir şekilde çağırdığınızı görün, baştan başlayın ve her şey yolunda

2) Çıktı dosyasını belirtmeyi unuttunuz. Bunun sonucunda farklı senaryolar ortaya çıkar:

a) Giriş bir kerede okunur. Daha sonra dönüşüm başlar ve sonuç yazılmalıdır, ancak bunun yerine bir hata alırsınız. Zamana bağlı olarak, kullanıcının beklemesi gerekir (işlenmesi gereken veri kütlesine bağlı olarak) bu can sıkıcı olabilir.

b) Giriş adım adım okunur. Daha sonra yazma işlemi hemen (1) 'deki gibi sonlanır ve kullanıcı yeniden başlar.

Özensiz denetimi olarak görülebilir OK bazı koşullarda. Tamamen kullanıcı tabanınıza ve niyetinizin ne olduğuna bağlıdır.

Ek olarak: Paranoyadan kaçınmalı ve çok fazla ikili kontrol yapmamalısınız.


0

Testlerin gereksiz olmadığını iddia ediyorum.

  • Girdi parametresi olarak dosya adı gerektiren iki ortak işleviniz var. Parametrelerini doğrulamak uygundur. İşlevler, işlevselliklerine ihtiyaç duyan herhangi bir programda potansiyel olarak kullanılabilir.
  • Dosya adı olması gereken iki bağımsız değişken gerektiren bir programınız var. Fonksiyonları kullanır. Programın parametrelerini kontrol etmesi uygundur.

Dosya adları iki kez kontrol edilirken, farklı amaçlarla kontrol ediliyor. Fonksiyonlara ait parametrelerin doğrulandığına güvenebileceğiniz küçük bir programda, fonksiyonlardaki kontroller gereksiz olarak değerlendirilebilir.

Daha sağlam bir çözüm, bir veya iki dosya adı doğrulayıcısına sahip olacaktır.

  • Bir giriş dosyası için, parametrenin okunabilir bir dosya belirttiğini doğrulamak isteyebilirsiniz.
  • Çıktı dosyası için, parametrenin yazılabilir bir dosya veya oluşturulabilen ve yazılabilen geçerli bir dosya adı olduğunu doğrulamak isteyebilirsiniz.

İşlemleri ne zaman yapacağım için iki kural kullanıyorum:

  • Onları mümkün olduğunca erken yapın. Bu, her zaman gerekli olacak şeyler için iyi çalışır. Bu programın bakış açısından, bu argv değerlerinin kontrolüdür ve program mantığındaki sonraki doğrulamalar gereksiz olacaktır. İşlevler bir kütüphaneye taşınırsa, kütüphane tüm arayanların parametreleri doğruladığına güvenemeyeceği için artık gereksiz değildir.
  • Onları mümkün olduğunca geç yapın. Bu, nadiren gerekecek şeyler için son derece iyi çalışır. Bu programın bakış açısından, bu fonksiyon parametreleri üzerindeki kontrollerdir.

0

Kontrol gereksizdir. Bu sorunu gidermek için, readFromInputFile ve writeToOutputFile öğelerini kaldırmanız ve bunları readFromStream ve writeToStream ile değiştirmeniz gerekir.

Kodun dosya akışını aldığı noktada, geçerli bir dosyaya bağlı geçerli bir akışınız olduğunu veya başka bir akışa bağlanabileceğinizi bilirsiniz. Bu gereksiz kontrolleri önler.

Daha sonra, akışı hala bir yerde açmanız gerektiğini sorabilirsiniz. Evet, ancak bu argüman ayrıştırma yönteminde dahili olarak gerçekleşir. Orada iki kontrolünüz var, biri dosya adının gerekli olup olmadığını kontrol etmek için, diğeri dosya adının işaret ettiği dosyanın verilen bağlamda geçerli dosya olup olmadığını kontrol etmek (örn. Girdi dosyası var, çıktı dizini yazılabilir). Bunlar farklı türde kontrollerdir, bu nedenle yedekli değildirler ve çekirdek uygulama yerine argüman ayrıştırma yönteminde (uygulama çevresi) gerçekleşirler.

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.