En İyi Uygulama - İşlev çağrısı etrafında sarma vs Muhafaza işlevinde ise erken çıkış ekleme


9

Bunun çok kullanışlı-duruma özgü olabileceğini biliyorum, ama kendimi bunu çok sık merak ediyorum. Genel olarak tercih edilen bir sözdizimi var mı?

Bir işlevdeyken en iyi yaklaşımın ne olduğunu sormuyorum, erken çıkmam mı yoksa işlevi çağırmamam gerektiğini soruyorum.

Wrap eğer etrafında işlev çağrısı


if (shouldThisRun) {
  runFunction();
}

Var ise ( bekçi ) işlevinde

runFunction() {
  if (!shouldThisRun) return;
}

İkinci seçenek, bu işlev birden çok kez çağrılırsa kod çoğaltmayı azaltma potansiyeline sahiptir, ancak bazen buraya eklemek yanlış olur, çünkü o zaman işlevin tek sorumluluğunu kaybediyor olabilirsiniz .


Heres bir örnek

Bir şeyin durumunu güncelleyen bir updateStatus () işlevim varsa. Sadece durum değiştiğinde durumun güncellenmesini istiyorum. Kodumda durumun değişme potansiyeline sahip olduğu yerleri biliyorum ve meydan okurcasına değiştiği diğer yerleri de biliyorum.

Sadece ben olup olmadığından emin değilim ama bu işlevi olabildiğince saf tutmak istediğim için bu iç işlevi kontrol etmek biraz kirli hissediyor - eğer ararsam durumun güncellenmesini bekliyorum. Ama aramayı bir kontrolle sarmanın daha iyi olup olmadığını anlayamıyorum, bildiğim birkaç yer değişme potansiyeline sahip.



3
@gnat Hayır, bu soru temelde 'Erken çıkışta tercih edilen sözdizimi nedir' oysa benimki 'Erken çıkmalı mıyım yoksa sadece işlevi çağırmamalı mıyım'
Matthew Mullin

4
işlevin ön koşullarını, arandığı her yerde doğru bir şekilde kontrol etmek için geliştiricilere, kendinize bile güvenemezsiniz . bu nedenle, eğer işlevsellik varsa fonksiyonun dahili olarak gerekli koşulları doğrulamasını öneririm.
TZHX

"Yalnızca durum değiştiğinde durumun güncellenmesini istiyorum" - aynı durum değiştiyse bir durumun güncellenmesini (= değişti) ister misiniz? Kulağa oldukça dairesel geliyor. Bununla ne demek istediğini açıklığa kavuşturabilir misin, böylece bu konudaki cevabım için anlamlı bir örnek ekleyebilir miyim?
Doc Brown

@DocBrown Örneğin, iki farklı nesnenin durum özelliklerini senkronize tutmak istediğimi söyleyelim. Nesnelerden herhangi biri değiştiğinde syncStatuses () çağrılır, ancak bu birçok farklı alan değişikliği için tetiklenebilir (yalnızca durum alanı değil).
Matthew Mullin

Yanıtlar:


15

Bir işlev çağrısının etrafına if sarma:
Bu, işlevin hiç çağrılması gerekip gerekmediğine karar vermek içindir ve programınızın karar verme sürecinin bir parçasıdır.

Görevde koruma maddesi (erken dönüş):
Bu, geçersiz parametrelerle çağrılmaya karşı korumak içindir

Bu şekilde kullanılan bir koruma maddesi, işlevi "saf" (teriminiz) tutar. Yalnızca işlevin hatalı giriş verileriyle bozulmamasını sağlamak için vardır.

İşlevin çağrılıp çağrılmayacağı mantığı, sadece olsa bile, daha yüksek bir soyutlama düzeyindedir. İşlevin dışında var olmalıdır . DocBrown'un dediği gibi, kodu basitleştirmek için bu kontrolü gerçekleştiren bir ara işleve sahip olabilirsiniz.

Bu, sormak için iyi bir soru ve soyutlama seviyelerinin tanınmasına yol açan bir dizi soruya giriyor. Her işlev soyutlama tek düzeyinde çalışmasına ve program mantığını ve fonksiyonundaki fonksiyon mantığı ikisine birden sahip size yanlış geliyor olmalı - onlar bunun nedeni olan soyutlama farklı düzeylerde. Fonksiyonun kendisi daha düşük bir seviyedir.

Bunları ayrı tutarak kodunuzun yazılmasını, okunmasını ve bakımını kolaylaştırırsınız.


Müthiş cevap. Bunu düşünmem için net bir yol verdiğini seviyorum. Eğer fonksiyon ilk etapta çağrılıp çağrılmayacağına dair 'karar verme sürecinin bir parçası' olduğu için if dışında olmalıdır. Ve doğal olarak işlevin kendisi ile ilgisi yoktur. Bir fikir yanıtını doğru olarak işaretlemek garip geliyor, ancak birkaç saat içinde tekrar bakacağım ve yapacağım.
Matthew Mullin

Eğer yardımcı olursa, bunu bir "görüş" yanıtı olarak görmüyorum. Bunun "yanlış" hissettirdiğine dikkat ediyorum, ancak bunun nedeni farklı soyutlama düzeylerinin ayrı olmamasıdır. Sorunuzdan aldığım şey, bunun 'doğru' olmadığını görebiliyorsunuz, ancak soyutlama seviyelerini düşünmediğiniz için, ölçmek zor, bu yüzden kelimelere dökmek için mücadele ettiğiniz bir şey.
Baldrickk

7

Her ikisine de sahip olabilirsiniz - parametreleri kontrol etmeyen bir işlev ve bunun gibi bir işlev daha (belki çağrı yapılıp yapılmadığı hakkında bazı bilgiler döndürme):

bool tryRunFunction(...)
{
    bool shouldThisRun = /* some logic using data not available inside "runFunction"*/;
    if (shouldThisRun)
        runFunction();
    return shouldThisRun;
}

Bu şekilde, tekrar kullanılabilir bir sağlayarak yinelenen mantıktan kaçınabilirsiniz tryRunFunctionve yine de kontrolün içinde yer almayan orijinal (belki de saf) fonksiyonunuza sahip olabilirsiniz.

Bazen tryRunFunctionyalnızca entegre bir kontrol gibi bir işleve ihtiyaç duyacağınızı unutmayın , böylece kontrolü entegre edebilirsiniz runFunction. Ya da kontrolü programınızda herhangi bir yerde tekrar kullanmanıza gerek yoktur, bu durumda arama işlevinde kalmasına izin verebilirsiniz.

Ancak, işlevlerinize uygun isimler vererek onu arayanlara şeffaf hale getirmeye çalışın. Bu nedenle, arayanların kontrolleri kendileri yapmaları gerekiyorsa veya çağrılan işlev zaten onlar için yapıyorsa, uygulamayı tahmin etmek veya uygulamaya bakmak zorunda değildir. Bunun için basit bir önek trygenellikle yeterli olabilir.


1
İtiraf etmeliyim ki, "tryXXX ()" deyimi her zaman biraz kapalı görünüyordu ve burada uygunsuz. Muhtemel bir hata bekleyen bir şey yapmaya çalışmıyorsunuz. Kirli ise güncelleme yapıyorsunuz.
user949300

@ user949300: iyi bir ad veya adlandırma şeması seçmek, gerçek kullanım durumuna, gerçek işlev adlarına, benzer bazı isimlere bağlı değildir runFunction. Benzeri updateStatus()bir işleve benzer başka bir işlev eşlik edebilir updateIfStatusHasChanged(). Ama bu% 100 vakaya bağlı, bunun için "tek bedene uygun" bir çözüm yok, bu yüzden evet, katılıyorum, "denemek" deyim her zaman iyi bir seçim değildir.
Doc Brown

"DryRun" diye bir şey yok mu? Az ya da çok yan etki olmadan düzenli bir uygulama haline gelir. Yan etkilerin nasıl devre dışı
bırakılacağı

3

Çalıştırmak için karar verir kimin gelince, cevabı itibaren ise GRASP "bilgi uzman" bilir.

Buna karar verdikten sonra, netlik için işlevi yeniden adlandırmayı düşünün.

İşlev karar verirse böyle bir şey:

 ensureUpdated()
 updateIfDirty()

Veya, arayanın karar vermesi gerekiyorsa:

 writeStatus()

2

@ Baldrickk'ın cevabını genişletmek istiyorum.

Sorunuza genel bir cevap yok. Aranacak işlevin anlamına (sözleşmesine) ve durumun doğasına bağlıdır.

Öyleyse bunu örnek çağrınız bağlamında ele alalım updateStatus(). Mutabakatı muhtemelen bazı statüleri güncellemektir çünkü statü üzerinde etkisi olan bir şey olmuştur. Gerçek bir durum değişikliği olmasa bile bu yöntemin çağrılarına izin verilmesini ve gerçek bir değişiklik olması durumunda gerekli olmasını beklerim.

Bu nedenle, arayan bir site, updateStatus()(etki alanı ufkunun içinde) alakalı hiçbir şeyin değişmediğini biliyorsa çağrıyı atlayabilir . Çağrının uygun bir ifyapı ile çevrelenmesi gereken durumlar budur .

İçinde updateStatus()fonksiyonu, yapacak bir şey var ki, o yıllardan (kendi alan ufkunun içine verilerden) bu fonksiyon algılar erken dönmelidir durumlar olabilir.

Yani, sorular:

  • Dış görünümden, işlevin sözleşmesini dikkate alarak işlevi çağırmaya ne zaman izin verilir / istenir?
  • Fonksiyonun içinde, gerçek bir iş yapmadan erken dönebileceği durumlar var mı?
  • İşlevin çağrılıp çağrılmayacağı / erken dönülüp dönülmeyeceği, işlevin dahili etki alanına mı yoksa dışa mı ait?

Bir updateStatus()işlevle, her ikisini de görmeyi beklerim, hiçbir şey değişmediğini bilen siteleri çağırır, çağrıyı atlar ve uygulama, bu şekilde aynı durum sometims iki kez kontrol edilse bile, her ikisi de "hiçbir şey değişmedi" durumlarını kontrol eder içte ve dışta.


2

Çok iyi açıklamalar var. Ama alışılmadık bir şekilde bakmak istiyorum: Bu şekilde kullandığınızı varsayın:

if (shouldThisRun) {
   runFunction();
}

runFunction() {
   if (!shouldThisRun) return;
}

Ve böyle bir runFunctionyöntemde başka bir işlevi çağırmanız gerekir :

runFunction() {
   if (!shouldThisRun) return;
   someOtherfunction();
}

Ne yapacaksın? Tüm doğrulamaları yukarıdan aşağıya kopyalıyor musunuz?

someOtherfunction() {
   if (!shouldThisRun) return;
}

Ben öyle düşünmüyorum. Bu nedenle, genellikle aynı yaklaşımı yaparım: Yöntemdeki girdileri ve kontrol koşullarını doğrulayın public. Genel yöntemler kendi doğrulamalarını yapmalı ve arayanların bile yapması gereken koşulları kontrol etmelidir. Ancak özel yöntemlerin kendi işini yapmasına izin verin . Başka bir işlev runFunctionherhangi bir doğrulama yapmadan veya herhangi bir koşulu kontrol etmeden arayabilir .

public someFunction() {
   if (shouldThisRun) {
      runFunction();
   }
}

private runFunction() {
 // do your business.
}
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.