Davranışı belirlemek için bir boolean parametresi kullanmak yanlış mı?


194

Zaman zaman "yanlış" hisseden "bir uygulama gördüm, ama yanlış olanı açıkça ifade edemiyorum. Ya da belki de sadece önyargım. İşte gidiyor:

Bir geliştirici, bir boole yöntemini parametrelerinden biri olarak tanımlar ve bu yöntem başkalarını çağırır ve böyle devam eder ve nihayetinde boole yalnızca belirli bir işlem yapılıp yapılmayacağını belirlemek için kullanılır. Bu, örneğin, yalnızca kullanıcının belirli haklara sahip olması durumunda veya belki de test modunda veya toplu modunda veya canlı modda olduğumuzda (ya da olmadığımızda) ya da belki sadece sistemdeyken harekete izin vermek için kullanılabilir. belirli bir devlet.

Peki, bunu yapmak için her zaman başka bir yol vardır, ister eylemin zamanı geldiğinde sorgulayarak (parametreyi geçmek yerine), ya da yöntemin birden çok versiyonuna veya sınıfın birden fazla uygulamasına sahip olarak, vs. Bunun nasıl geliştirileceği çok fazla değil, bunun yerine gerçekten yanlış olup olmadığına bakalım (şüphelendiğim gibi) ve eğer öyleyse, bu konuda yanlış olan ne?


1
Bu, kararların nereye ait olduğu sorusudur. Kararları her yere çöp atmak yerine, merkezi bir yere taşıyın. Bu, karmaşıklığı, her zaman bir ifere sahip olduğunuzda iki kod yolu faktöründen daha düşük tutar.

28
Martin Fowler'ın bununla ilgili bir makalesi var: martinfowler.com/bliki/FlagArgument.html
Christoffer Hammarström


@ ChristofferHammarström Güzel link. Bunu, açıklamamdaki aynı fikri çok ayrıntılı olarak açıkladığım gibi ekleyeceğim.
Alex,

1
Nick'in söyleyeceklerine her zaman katılıyorum, ancak bu durumda% 100 katılıyorum: Boole parametreleri kullanmayın .
Marjan Venema,

Yanıtlar:


107

Evet, bu muhtemelen bir kod kokusudur, anlaşılması zor olan ve kolayca yeniden kullanılma şansı daha düşük olan ve sürdürülemez bir koda yol açacaktır.

Diğer afişlerin de belirttiği gibi , her şey ( her şeyden önce kapalıysa ya da uygulamanın kasıtlı olarak yapılmış teknik borç olarak kabul edildiyse kabul edildiyse ağır elden gitme), ancak daha sonra geçirilmiş bir parametre varsa genel olarak konuşun yürütülecek spesifik davranışı seçen bir fonksiyonun içine daha sonra adım adım iyileştirme yapılması gerekir; Bu işlevi daha küçük işlevlere bölmek, daha yüksek yapılı işlevler üretecektir.

Peki yüksek yapışma işlevi nedir?

Bir şeyi ve sadece bir şeyi yapan bir işlev.

Tanımladığınız gibi girilen bir parametreyle ilgili sorun, işlevin ikiden fazla şey yapmasıdır; Boolean parametresinin durumuna bağlı olarak kullanıcıların erişim haklarını kontrol edebilir veya etmeyebilir, daha sonra bu karar ağacına bağlı olarak bir işlevsellik gerçekleştirir.

Erişim Kontrolü endişelerini Görev, Eylem veya Komuta endişelerinden ayırmak daha iyi olacaktır.

Daha önce not ettiğiniz gibi, bu kaygıların iç içe geçmesi görünmektedir.

Bu nedenle, Yapışkanlık kavramı, söz konusu işlevin yüksek düzeyde yapışma olmadığını ve daha fazla sayıda yapışma işlevi üretmek için kodu yeniden düzenleyebileceğimizi belirlememize yardımcı olur.

Böylece soru yeniden ifade edilebilir; Hepimizin davranış seçimi seçiminden geçmek konusunda hemfikir olduğumuz göz önüne alındığında, en iyi şekilde kaçınılması durumunda sorunları nasıl geliştiririz?

Parametreden tamamen kurtulurdum. Test için bile erişim kontrolünü kapatma yeteneğine sahip olmak, potansiyel bir güvenlik riskidir. Test amacıyla , hem izin verilen erişimi hem de erişim reddedilen senaryoları test etmek için erişim kontrolünü veya edin.

Ref: Uyum (bilgisayar bilimi)


Rob, Uyumun ne olduğuna ve nasıl uygulandığına dair bir açıklama yapar mısın?
Ray,

Ray, bu konuda ne kadar çok düşünürsem, erişim kontrolünü açmak için booleanın güvenlik deliğine dayalı koda itiraz etmeniz gerektiğini düşünüyorum. Kod tabanındaki kalite iyileştirmesi güzel bir yan etki olacaktır;)
Rob

1
Uyumun çok güzel bir açıklaması ve uygulaması. Bu gerçekten daha fazla oy almalı. Güvenlik konusuna da katılıyorum ... ancak hepsi de özel yöntemse bu daha küçük bir potansiyel güvenlik açığıdır
Ray

1
Sağol Ray. Zaman elverdiğinde, yeniden faktörlendirme yapmak yeterince kolay olacak gibi görünüyor. Konuyu vurgulamak için TODO yorumunu bırakmaya değer olabilir, teknik otorite ile bazen yapılması gereken baskıya duyarlılık arasında bir denge kurarak işleri halledebiliriz.
Rob,

"ulaşılamaz"
aaa90210

149

Çok basit bir nedenden ötürü bu kalıbı uzun süre önce kullanmayı bıraktım; bakım maliyeti. Birkaç kez frobnicate(something, forwards_flag), kodumda birçok kez çağrılan ve değerin değeri falseolarak iletilen koddaki tüm yerleri bulmam gerektiğini söyleyen bazı işlevler olduğunu öğrendim forwards_flag. Bunları kolayca arayamazsınız, bu da bakım baş ağrısına dönüşür. Ve bu sitelerin her birinde bir hata düzeltmeniz gerekiyorsa, birini kaçırırsanız talihsiz bir sorunla karşılaşabilirsiniz.

Ancak bu spesifik sorun, yaklaşımı temelden değiştirmeden kolayca çözülebilir:

enum FrobnicationDirection {
  FrobnicateForwards,
  FrobnicateBackwards;
};

void frobnicate(Object what, FrobnicationDirection direction);

Bu kodla, yalnızca bir örneğini aramanız gerekir FrobnicateBackwards. Bunu bir değişkene atayan bazı kodlar olması mümkün olsa da, birkaç kontrol dizisini izlemeniz gerekir, pratikte bu alternatifin işe yarayacak kadar nadir olduğunu gördüm.

Ancak, bayrağı bu şekilde, en azından prensipte geçirirken başka bir sorun var. Bu, bu tasarıma sahip bazı (sadece bazı) sistemlerin, kodun iç içe geçmiş bölümlerinin (bayrağı kullanan) dış katmanlara (bayrağını kullanan) dış katmanlara (hangi değeri geçeceğini bilmesi gereken) uygulama detayları hakkında çok fazla bilgi açığa çıkarabileceğidir. Bu bayrakta). Larry Constantine'nin terminolojisini kullanmak için, bu tasarım belirleyici ve Boole bayrağının kullanıcısı arasında aşırı güçlü bir bağlantıya sahip olabilir . Franky, kod temeli hakkında daha fazla bir şey bilmeden bu konuda herhangi bir kesinlikte kesin olarak söylemesi zor olsa da.

Verdiğiniz belirli örnekleri ele almak için, her ikisinde de bir dereceye kadar kaygı duyuyorum ancak esas olarak risk / doğruluk nedenleriyle. Diğer bir deyişle, sisteminizin hangi durumda olduğunu gösteren bayrakların etrafından geçmesi gerekiyorsa, bunu hesaba katması gereken, ancak parametreyi kontrol etmeyen kodunu aldığınızı görebilirsiniz (çünkü bu işlev). Demek ki bir hata var çünkü birisi parametreyi geçemez.

Neredeyse her işleve geçirilmesi gereken bir sistem durumu göstergesinin aslında küresel bir değişken olduğunu kabul etmek de önemlidir. Küresel bir değişkenin olumsuz taraflarının çoğu geçerli olacaktır. Birçok durumda, sistem durumu bilgisini (veya kullanıcının kimlik bilgilerini veya sistemin kimliğini) bu verilere dayanarak doğru hareket etmekten sorumlu olan bir nesne içinde saklamak daha iyi olur. Sonra ham verinin tersine o nesneye yapılan bir referansı iletirsiniz. Buradaki anahtar kavram kapsüllemedir .


9
Gerçekten harika somut örnekler ve aynı zamanda uğraştığımız şeyin doğası ve bizi nasıl etkilediği hakkında bir fikir edin.
Ray

33
+1. Bunun için mümkün olduğunca enumsiyon kullanıyorum. Daha boolsonra ilave parametrelerin eklendiği fonksiyonları gördüm ve çağrılar benzemeye başladı DoSomething( myObject, false, false, true, false ). Fazladan boolean argümanların ne anlama geldiğini anlamak imkansızdır, oysa anlamlı olarak adlandırılmış enum değerleri ile kolaydır.
Graeme Perrow

17
Ah dostum, nihayet FrobnicateBackwards'a nasıl bir örnek. Sonsuza dek bunu aradım.
Alex Pritchard

38

Bu mutlaka yanlış değildir, ancak bir kod kokusunu temsil edebilir .

Boole parametreleriyle ilgili olarak kaçınılması gereken temel senaryo şöyledir:

public void foo(boolean flag) {
    doThis();
    if (flag)
        doThat();
}

Sonra aradığınızda genellikle arayacak foo(false)ve foo(true)istediğiniz tam davranışa bağlı olarak.

Bu gerçekten bir sorun çünkü kötü bir uyum durumu. Gerçekten gerekli olmayan yöntemler arasında bir bağımlılık yaratıyorsunuz.

Ne bu durumda yapması gereken ayrılıyor doThisve doThatayrı ve kamu yöntemleri gibi daha sonra yapıyor:

doThis();
doThat();

veya

doThis();

Bu şekilde arayana doğru bir karar bırakırsınız (aynen bir boole parametresini geçiyormuşsunuz gibi).

Tabii ki tüm boolean parametreler o kadar kötü bir şekilde kullanılmıyor, ancak kesinlikle bir kod kokusu ve kaynak kodunda çok şey görürseniz şüphelenmekte haklısınız.

Bu, yazdığım örneklere dayanarak bu sorunun nasıl çözüleceğine sadece bir örnektir. Farklı bir yaklaşımın gerekli olacağı başka durumlar da vardır.

Martin Fowler'tan aynı fikri daha ayrıntılı olarak açıklayan iyi bir makale var .

Not: Eğer fooiki basit yöntemi çağırmak yerine yöntem daha karmaşık bir uygulamaya sahipse, yapmanız gereken tek şey küçük bir yeniden düzenleme yöntemini kullanmaktır; böylece ortaya çıkan kod fooyazdığımın uygulamasına benzer .


1
"Kod kokusu" terimini çağırdığınız için teşekkürler - Kötü koktuğunu biliyordum ama kokunun ne olduğunu tam olarak anlayamadım. Örneğiniz neye baktığım konusunda oldukça açık.
Ray

24
if (flag) doThat()İçeride foo()meşru olduğu birçok durum var . doThat()Her arayanı çağırmakla ilgili kararı zorlamak, daha sonra bazı yöntemler öğrenirseniz kaldırılması gereken tekrarı zorlar, flagdavranışın da çağrılması gerekir doTheOther(). Daha sonra tüm arayanları araştırmaya gitmek yerine, aynı sınıftaki yöntemler arasında bağımlılıklar koymayı tercih ederim.
Blrfl

1
@Blrfl: Evet, daha anlaşılır refactorings olacağını düşünüyorum ya bir yaratma doOneve doBothJames Youngman tarafından önerildiği gibi, (sırasıyla, yalancı ve gerçek durum için) yöntemleri veya ayrı enum türü kullanarak
hugomg

@missingno: Gereksiz kodu tekrar aramak doOne()veya doBoth()karar vermek için arayanlara verme zorunluluğunuz var . Alt yordamlar / fonksiyonlar / yöntemler argümanlara sahiptir, böylece davranışları değişebilir. Gerçekten bir Boolean koşulu için enum kullanmak, argümanın adı ne yaptığını açıklarsa, kendinizi tekrar etmek gibi görünüyorsun.
Blrfl

3
Eğer iki metodu birbiri ardına veya bir metodu tek tek çağırmak gereksiz sayılırsa, bir try-catch bloğu veya belki bir if if nasıl yazdığınız da gereksizdir. Hepsini soyutlamak için bir fonksiyon yazacağın anlamına mı geliyor? Hayır! Dikkatli olun, her şeyin diğer iki yöntemi çağırdığı bir yöntem oluşturmak, mutlaka iyi bir soyutlamayı temsil etmez.
Alex

29

Öncelikle: programlama, bir sanat olduğu kadar bir bilim değildir. Bu nedenle programın nadiren "yanlış" ve "doğru" bir yolu vardır. Çoğu kodlama standardı, yalnızca bazı programcıların faydalı olduğunu düşündüğü “tercihler” dir; ama sonuçta onlar keyfi. Bu yüzden, hiçbir zaman kendi içinde "yanlış" olacak bir parametre seçimini etiketlemem - kesinlikle bir boolean parametresi kadar genel ve kullanışlı bir şey değildir. Bir durumu boolean(veya bir intmaddenin bu durumu kapsüllemek için) kullanımı, çoğu durumda mükemmel bir şekilde haklı gösterilebilir.

Kodlama kararları, genel olarak performans ve sürdürülebilirliğe dayanmalıdır. Performans tehlikede değilse (ve örneklerinizde nasıl olabileceğini hayal bile edemiyorum), bir sonraki düşünceniz şu şekilde olmalı: bu benim (veya gelecekteki bir redaktörün) bakımı için ne kadar kolay olacak? Sezgisel ve anlaşılır mı? İzole edilmiş mi? Zincirleme işlev çağrılarının Kişisel örneği aslında bu açıdan potansiyel olarak kırılgan görünüyor yapar: Eğer değiştirmeye karar verirseniz sizin bIsUpiçin bIsDown, kaç başka yerlerde kodunda çok değişti gerekir? Ayrıca, paramater listeniz balon mu? İşlevinizde 17 parametre varsa, okunabilirlik bir sorundur ve nesne yönelimli mimarinin faydalarını takdir edip etmediğinizi yeniden düşünmeniz gerekir.


4
İlk paragraftaki uyarıyı takdir ediyorum. "Yanlış" diyerek kasıtlı olarak provokatif davranıyordum ve kesinlikle "en iyi uygulamalar" ve tasarım ilkeleri alanında bulunduğumuzu ve bu tür şeylerin genellikle durumsal olduğunu ve birinin birden fazla faktörü tartıştığını kabul ediyorum
Ray

13
Cevabınız bana, "işlevinde 17 parametre varsa, muhtemelen bir tane eksik" kaynağını hatırlayamadığım bir alıntı hatırlatıyor.
Joris Timmermans,

Buna çok katılıyorum ve bir evet, bir boolean bayrağına geçmek için genellikle kötü bir fikir olduğunu söylemek sorusuna başvurdum, ancak hiçbir zaman kötü / iyi kadar basit değil ...
JohnB

15

Bence Robert C Martins Clean kodu makalesi , bir yöntemin birden fazla şey yaptığını gösterdiklerinde mümkün olduğunda boolean argümanlarını ortadan kaldırmanız gerektiğini belirtir. Bir yöntem bir şey yapmalı ve sadece bence onun sloganlarından biri.


@ dreza, Curlys Yasası'ndan bahsediyorsunuz .
MattDavey

Tabii ki tecrübeyle bu tür argümanların ne zaman yoksayılacağını bilmelisin.
gnasher729

8

Bence buradaki en önemli şey pratik olmak.

Boolean davranışın tamamını belirlediğinde, sadece ikinci bir yöntem yapın.

Boole ortada yalnızca biraz davranış belirlediğinde, kod çoğaltmayı azaltmak için bir tanesinde tutmak isteyebilirsiniz. Mümkün olduğunda, yöntemi üçe bölebilirsiniz: Her iki boolean seçeneği için iki çağrı yöntemi ve biri işin büyük kısmını yapan.

Örneğin:

private void FooInternal(bool flag)
{
  //do work
}

public void Foo1()
{
  FooInternal(true);
}

public void Foo2()
{
  FooInternal(false);
}

Tabii ki, pratikte bu aşırı uçlar arasında her zaman bir noktaya sahip olacaksınız. Genelde sadece doğru olanı giderim, ama daha az kod çoğaltmanın yanında hata yapmayı tercih ederim.


Özel yöntemlerde (burada gösterildiği gibi) davranışı denetlemek için yalnızca boolean değişkenleri kullanıyorum. Ancak sorun: Bazı dufus FooInternalgelecekte görünürlüğünü arttırmaya karar verirse ne olacak?
ADTC

Aslında başka bir rotaya giderdim. FooInternal içerisindeki kod 4 parçaya bölünmelidir: 2 Boolean doğru / yanlış durumlarını ele almak için, biri daha önce gerçekleşen iş için diğeri de daha sonra gerçekleşen iş için kullanılır. Sonra, senin Foo1olur: { doWork(); HandleTrueCase(); doMoreWork() }. İdeal olarak, doWorkve doMoreWorkfonksiyonlarının her biri, bölünme amacıyla sadece iki fonksiyona değil, (bir veya daha fazla) anlamlı ayrık eylem parçalarına (yani ayrı fonksiyonlar) ayrılır.
jpaugh

7

Değişmez örnekleri döndüren oluşturucu yöntemlerle davranış özelleştirme yaklaşımını seviyorum. İşte GuavaSplitter bunu nasıl kullanıyor:

private static final Splitter MY_SPLITTER = Splitter.on(',')
       .trimResults()
       .omitEmptyStrings();

MY_SPLITTER.split("one,,,,  two,three");

Bunun faydaları:

  • Üstün okunabilirlik
  • Yapılandırma ve eylem yöntemlerinin açık bir şekilde ayrılması
  • Sizi, nesnenin ne olduğu, ne yapması ve yapmaması gerektiği hakkında düşünmeye zorlayarak uyumu arttırır. Bu durumda bir Splitter. Asla someVaguelyStringRelatedOperation(List<Entity> myEntities)denilen bir sınıfa girmediniz Splitter, ancak bunu StringUtilssınıfa statik bir yöntem olarak koymayı düşünürdünüz .
  • Örnekler önceden yapılandırılmıştır, bu nedenle kolayca bağımlılık enjekte edilebilir. Müşterinin doğru davranışı elde etmek için geçip geçmemek trueya falseda bir yöntem hakkında endişelenmesi gerekmez .

1
Büyük bir Guava aşığı ve evangelist olarak çözümünüze kısmi geldim ... ama size + 1 veremem, çünkü gerçekten aradığım kısmı atlıyorsunuz, ki bu yanlış (veya koklamak) diğer yolla. Bunun bazı açıklamalarınızda aslında açık olduğunu düşünüyorum, bu yüzden belki de bunu açıkça yapabilirsiniz, soruyu daha iyi cevaplardı.
Ray,

Yapılandırma ve akton yöntemlerini ayırma fikrini seviyorum!
Sher10ck

Guava kütüphanesine bağlantılar koptu
Josh Noe

4

Kesinlikle bir kod kokusu . O ihlal etmiyorsa tek sorumluluk prensibi o zaman muhtemelen ihlal Do not Ask, söyle . Düşünmek:

Bu iki ilkeden birini ihlal etmediği ortaya çıkarsa, yine de enum kullanmalısınız. Boolean bayrakları, sihirli sayıların boolean karşılığıdır . foo(false)kadar mantıklı bar(42). Enums, Strateji Deseni için faydalı olabilir ve başka bir strateji eklemenize izin verme esnekliğine sahip olabilir. (Sadece onları uygun şekilde adlandırmayı unutmayın .)

Özel örneğiniz özellikle beni rahatsız ediyor. Bu bayrak neden bu kadar çok yöntemden geçiyor? Bu , parametrenizi alt sınıflara ayırmanız gerektiği gibi geliyor .


4

TL; DR: boolean argümanlarını kullanmayın.

Neden kötü olduklarını ve nasıl değiştirileceğini (koyu renkte) aşağıya bakın .


Boolean argümanlarının okunması çok zor ve bu nedenle de sürdürülmesi zor. Asıl sorun, argümanın isimlendirildiği yöntem imzasını okuduğunuzda, amaç genellikle açıktır. Bununla birlikte, çoğu dilde bir parametrenin adlandırılması genellikle gerekli değildir. Böylece RSACryptoServiceProvider#encrypt(Byte[], Boolean), boolean parametresinin, fonksiyonda ne tür şifreleme kullanılacağını belirlediği gibi anti-paternlere sahip olacaksınız .

Böylece şöyle bir çağrı alacaksınız:

rsaProvider.encrypt(data, true);

Okuyucunun, sadece cehennemin ne trueanlama gelebileceğini belirlemek için yöntemin imzasını araması gerekir . Bir tamsayı geçmek elbette ki kötü:

rsaProvider.encrypt(data, 1);

size söyleyeceğim kadar çok - daha doğrusu: çok az. Tamsayı için kullanılacak sabitleri tanımlasanız bile, işlevin kullanıcıları basitçe bunları görmezden gelebilir ve değişmez değerleri kullanmaya devam edebilir.

Bunu çözmenin en iyi yolu bir numaralandırma kullanmaktır . RSAPaddingİki değeri olan bir enum geçmek zorunda kalırsanız : OAEPveya PKCS1_V1_5hemen kodu okuyabilirsiniz:

rsaProvider.encrypt(data, RSAPadding.OAEP);

Booleanların yalnızca iki değeri olabilir. Bu, üçüncü bir seçeneğiniz varsa, imzanızı yeniden düzenlemek zorunda kalacağınız anlamına gelir. Genel olarak, geriye dönük uyumluluk bir sorunsa, bu kolayca gerçekleştirilemez, bu nedenle herhangi bir genel sınıfı başka bir genel yöntemle genişletmeniz gerekir. Microsoft RSACryptoServiceProvider#encrypt(Byte[], RSAEncryptionPadding), bir boole yerine bir numaralandırma (veya en azından bir numaralandırmayı taklit eden bir sınıf) kullandıkları yeri tanıttıklarında nihayet yaptıkları buydu .

Parametrenin kendisinin parametrelenmesi gerektiğinde parametre olarak tam bir nesne veya arayüz kullanmak daha kolay olabilir . Yukarıdaki örnekte OAEP dolgusunun kendisi dahili olarak kullanılacak karma değerle parametrelendirilebilir. Şimdi 6 SHA-2 karma algoritması ve 4 SHA-3 karma algoritması olduğunu unutmayın; bu nedenle, parametreler yerine yalnızca tek bir sayma kullanırsanız sayma değerlerinin sayısı artabilir (bu, Microsoft'un öğreneceği bir sonraki şeydir. ).


Boolean parametreleri ayrıca yöntemin veya sınıfın iyi tasarlanmadığını gösterebilir. Yukarıdaki örnekte olduğu gibi: .NET dışındaki herhangi bir şifreleme kitaplığı, yöntem imzasında bir doldurma bayrağı kullanmaz.


Neredeyse tüm sevdiğim yazılım guruları boolean argümanlara karşı uyarıyorlar. Mesela Joshua Bloch, çok beğenilen kitabı "Effective Java" ile onlara karşı uyardı. Genelde onlar kullanılmamalıdır. Anlaşılması kolay bir parametre olması durumunda kullanılabileceğini iddia edebilirsiniz. Ancak o zaman bile: Bit.set(boolean)Muhtemelen iki yöntem kullanılarak daha iyi uygulanır : Bit.set()ve Bit.unset().


Doğrudan kodu yeniden aktive edemiyorsanız , en azından daha okunaklı hale getirmek için sabitleri tanımlayabilirsiniz :

const boolean ENCRYPT = true;
const boolean DECRYPT = false;

...

cipher.init(key, ENCRYPT);

şundan çok daha okunur:

cipher.init(key, true);

sahip olsanız bile:

cipher.initForEncryption(key);
cipher.initForDecryption(key);

yerine.


3

Hiç kimsenin adlandırılmış parametrelerden bahsetmediğine şaşırdım .

Boolean bayrakları ile gördüğüm sorun onların okunabilirliği incitiyor olmasıdır. Örneğin, ne yaptığını trueiçinde

myObject.UpdateTimestamps(true);

yap? Hiç bir fikrim yok. Ama ne hakkında:

myObject.UpdateTimestamps(saveChanges: true);

Şimdi, geçtiğimiz parametrenin ne anlama geldiği açıktır: işleve değişiklikleri kaydetmesini söylüyoruz. Bu durumda, sınıf herkese açık değilse, boolean parametresinin iyi olduğunu düşünüyorum.


Tabii ki, sınıfınızdaki kullanıcıları adlandırılmış parametreleri kullanmaya zorlayamazsınız. Bu nedenle, enumvarsayılan durumunuzun ne kadar yaygın olduğuna bağlı olarak genellikle bir veya iki ayrı yöntem tercih edilir. Bu tam olarak ne olduğunu. Net yapar:

//Enum used
double a = Math.Round(b, MidpointRounding.AwayFromZero);

//Two separate methods used
IEnumerable<Stuff> ascendingList = c.OrderBy(o => o.Key);
IEnumerable<Stuff> descendingList = c.OrderByDescending(o => o.Key); 

1
Soru böyle bir bayrak olmadığını bir koku ve eğer öyleyse, neden, bir davranış belirleme bayrak tercih edilir ilgili değil
Ray

2
@Ray: Bu iki soru arasında bir fark görmüyorum. Adlandırılmış parametrelerin kullanımını zorlayabileceğiniz bir dilde veya adlandırılmış parametrelerin her zaman kullanılacağından emin olduğunuzda (örneğin , özel yöntemler) , boolean parametreler iyidir. Adlandırılmış parametreler, dil (C #) tarafından uygulanamazsa ve sınıf, genel bir API'nin parçasıysa veya dil, adlandırılmış parametreleri (C ++) desteklemiyorsa, bu gibi bir kod myFunction(true)yazılabilir. koku.
BlueRaja - Danny Pflughoeft 10:12

Adlandırılmış parametre yaklaşımı daha da yanlıştır. Bir ad olmadan, API dokümanını okumak zorunda kalacaksınız. Bir isimle, ihtiyacın olmadığını düşünüyorsun: ama paramter yanlış isim olabilir. Örneğin (tüm) değişiklikleri kaydetmek için kullanılmış olabilir, ancak daha sonra uygulama yalnızca büyük değişiklikleri (büyük bir değer için) kaydetmek için biraz değişti .
Ingo

@Ingo Katılmıyorum. Bu genel bir programlama sorunu; kolayca başka bir SaveBigisim tanımlayabilirsiniz . Herhangi bir kod berbat edilebilir, bu tür bir vidalama, adlandırılmış parametrelere özel değildir.
Maarten Bodewes

1
@Ingo: Eğer salaklarla çevriliysen, o zaman git ve başka bir yerde iş bul. Bu tür bir şey için kod incelemeleriniz var.
gnasher729

1

Bu konuda neyin yanlış olduğunu açıkça ifade edemiyorum.

Bir kod kokusuna benziyorsa, bir kod kokusuna benziyor ve - iyi - bir kod kokusuna benziyor, muhtemelen bir kod kokusuna benziyor.

Yapmak istediğin şey:

1) Yan etkileri olan yöntemlerden kaçının.

2) gibi bir merkezi, yaygın durum makinesi (gerekli durumları Kolu Bu ).


1

Performansı belirlemek için Boole Parametrelerini kullanmanın tüm endişelerini kabul ediyorum; iyileştirin, Okunabilirlik, Güvenilirlik, Karmaşıklığı Düşürme, Kötü Kapsülleme ve Uyumluluk risklerini azaltın ve Bakım Kolaylığı ile Toplam Sahip Olma Maliyetini düşürün.

70'lerin ortalarında şimdi SCADA (denetim kontrolü ve veri toplama) olarak adlandırdığımız donanım tasarlamaya başladım ve bunlar EPROM'daki makro uzaktan kumandaları çalıştıran ve yüksek hızlı veri toplayan makine kodlu ince ayarlı donanımlardı.

Mantığa şu anda Sonlu Durumlu Makineler dediğimiz Mealey & Moore makineleri deniyor . Bunlar, sınırlı bir yürütme süresine sahip gerçek zamanlı bir makine olmadıkça ve aynı zamanda bu amaca hizmet etmek için kısayollar yapılması gerekmedikçe, yukarıdakilerle aynı kurallarda yapılmalıdır.

Veriler senkronizedir, ancak komutlar asenkrondir ve komut mantığı hafızasızlık Boolean mantığını izler ancak önceki, şimdiki ve istenen bir sonraki durumun hafızasına dayanan sıralı komutlarla. Bunun en verimli makine dilinde (sadece 64kB) çalışması için, her süreci bir sezgisel IBM HIPO tarzında tanımlamaya büyük özen gösterilmiştir. Bu bazen Boolean değişkenlerini geçmek ve indekslenmiş dallar yapmak anlamına geliyordu.

Ancak şimdi çok fazla bellek ve OOK kolaylığı ile Kapsülleme bugün önemli bir bileşendir, ancak kodun gerçek zamanlı olarak bayt olarak sayılması ve SCADA makine kodu.


0

Bu mutlaka yanlış değildir, ancak somut örneğinizde "kullanıcı" nın bazı niteliklerine bağlı olarak, bir bayraktan ziyade kullanıcıya yapılan bir referanstan geçeceğim.

Bu, çeşitli şekillerde netleşir ve yardımcı olur.

Çağrılı ifadeyi okuyan herkes sonucun kullanıcıya bağlı olarak değişeceğini fark edecektir.

Sonuç olarak adlandırılan işlevde, daha karmaşık iş kurallarını kolayca uygulayabilirsiniz, çünkü kullanıcı özelliklerinden herhangi birine erişebilirsiniz.

"Zincir" deki bir işlev / yöntem, bir kullanıcı özelliğine bağlı olarak farklı bir şey yaparsa, kullanıcı özelliklerine benzer bir bağımlılığın "zincir" deki diğer yöntemlerden bazılarına dahil olması muhtemeldir.


0

Çoğu zaman bu kötü kodlamayı düşünürdüm. Bununla birlikte, bunun iyi bir uygulama olabileceği iki durumu düşünebilirim. Neden kötü olduğunu söyleyen birçok cevap olduğu için, iyi olabileceği durumlarda iki kez teklif ediyorum:

İlki, sekanstaki her çağrının kendi başına mantıklı olmasıdır. Bu çağırarak kod eğer mantıklıdır olabilir yanlıştan doğruya veya false true değiştirilebilir, ya da denilen yöntem ise olabilir geçirmeden yerine doğrudan boolean parametresini kullanmak değiştirilebilir. Üst üste bu tür on çağrı olasılığı düşüktür, ancak olabilirdi ve eğer iyi bir programlama uygulaması olurdu.

İkinci durum, bir boolean ile uğraştığımız göz önüne alındığında biraz gerginlik. Ancak, programın birden fazla iş parçacığı veya olayı varsa, parametrelerin iletilmesi iş parçacığı / olaya özgü verileri izlemenin en kolay yoludur. Örneğin, bir program iki veya daha fazla soketten giriş alabilir. Bir soket için çalışan kodun uyarı mesajları üretmesi gerekebilir ve bir başkası için çalışan bir kod gerekmeyebilir. Daha sonra (çeşit) bir uyarı yönteminin, uyarı mesajlarının üretilebileceği yerlere iletilmesi için çok yüksek bir seviyeye ayarlanmış bir boolean için anlamlı olur. Çok sayıda iş parçacığı ya da bir araya getirilmiş olayların her birinin kendi değerine ihtiyacı olacağı için, veriler genel olarak herhangi bir türden büyük zorluklar dışında saklanamaz.

Emin olmak için, ikinci durumda muhtemelen kimin yalnızca içerik bir boolean bir sınıf / yapı oluşturmak istiyorum ve pas o etrafında yerine. Mesela çok uzun zaman önce başka alanlara ihtiyaç duyacağımdan emin olabilirdim - örneğin uyarı mesajlarının nereye gönderileceği gibi .


Enum, WARN, SILENT, yazdığınız gibi bir sınıf / yapı kullanırken bile anlamlı olacaktır (yani bir bağlam ). Ya da gerçekten sadece günlüğü harici olarak yapılandırın - hiçbir şey iletmenize gerek yok.
Maarten Bodewes

-1

Bağlam önemlidir. Bu tür yöntemler iOS'ta oldukça yaygındır. Sık kullanılan bir örnek olarak, UINavigationController yöntemi sağlar -pushViewController:animated:ve animatedparametre bir BOOL olur. Yöntem esas olarak aynı işlevi yerine getirir, ancak YES'e geçerseniz bir görünüm denetleyiciden diğerine geçişi canlandırır ve HAYIR alamazsanız yapmaz. Bu tamamen makul görünüyor; Bunun yerine iki yöntem sunmak zorunda kalmanız aptalca olacaktır, böylece animasyonu kullanıp kullanmayacağınıza karar verebilirsiniz.

Objective-C'de bu tür bir şeyi doğrulamak daha kolay olabilir; burada yöntem adlandırma sözdizimi, her parametre için C ve Java gibi dillerde aldığınızdan daha fazla bağlam sağlar. Bununla birlikte, tek bir parametre alan bir yöntemin kolayca boolean olabileceğini ve hala mantıklı olabileceğini düşünüyorum:

file.saveWithEncryption(false);    // is there any doubt about the meaning of 'false' here?

21
Aslında örnekte ne falseanlama geldiğine dair hiçbir fikrim yok file.saveWithEncryption. Şifreleme olmadan tasarruf edeceği anlamına mı geliyor? Öyleyse, neden dünyada yöntem adı "şifreleme ile" haklı olsun ki? Bunun gibi bir yöntemi olduğunu anlayabiliyordum save(boolean withEncryption), ancak gördüğümde file.save(false), parametrenin şifrelemeli veya şifresiz olacağını gösterdiği bir bakışta belli değil. Aslında, bu, James Youngman'ın bir enum kullanmak konusunda ilk noktası olduğunu düşünüyorum.
Ray

6
Alternatif bir hipotez, falseaynı isimde varolan herhangi bir dosyayı geçersiz kılma anlamına gelir. Tartışılan örnek biliyorum, ancak fonksiyonun belgelerine (veya koduna) bakmanız gerekeceğinden emin olmak için.
James Youngman

5
saveWithEncryptionBazen şifreleme ile kaydetmeyen bir yöntem bir hatadır. Olası olmalı file.encrypt().save()ya da Javas gibi olmalı new EncryptingOutputStream(new FileOutputStream(...)).write(file).
Christoffer Hammarström

2
Aslında, söylediklerinden başka bir şey yapan kod çalışmaz ve bu nedenle bir hatadır. saveWithEncryption(boolean)Bazen şifrelemeden tasarruf yapan bir yöntem yapmak için uçmaz , tıpkı bazen şifrelemeyle tasarruf eden bir yöntem yapmak için uçmaz saveWithoutEncryption(boolean).
Christoffer Hammarström

2
Bu yöntem kötü, çünkü açıkça "burada" yanlış "ın anlamı hakkında şüphe var". Neyse, böyle bir yöntemi asla ilk baştan yazmam. Kaydetmek ve şifrelemek ayrı eylemlerdir ve bir yöntem bir şey yapmalı ve iyi yapmalı. Nasıl yapılacağı hakkında daha iyi örnekler için önceki yorumlarıma bakın.
Christoffer Hammarström
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.