Temiz Kod - Değişmez 1'i sabit olarak değiştirmeli miyim?


14

Büyülü rakamlardan kaçınmak için, sık sık anlamlı bir isim vermemiz gerektiğini duyarız. Gibi:

//THIS CODE COMES FROM THE CLEAN CODE BOOK
for (int j = 0; j < 34; j++) {
    s += (t[j] * 4) / 5;
}

-------------------- Change to --------------------

int realDaysPerIdealDay = 4;
const int WORK_DAYS_PER_WEEK = 5;
int sum = 0;
for (int j = 0; j < NUMBER_OF_TASKS; j++) {
    int realTaskDays = taskEstimate[j] * realDaysPerIdealDay;
    int realTaskWeeks = (realdays / WORK_DAYS_PER_WEEK);
    sum += realTaskWeeks;
}

Ben böyle bir kukla yöntemi var:

Açıkla: Sanırım hizmet vereceğim bir liste insanım var ve varsayılan olarak, sadece yiyecek almak için 5 dolar harcıyoruz, ancak birden fazla kişi olduğunda, su ve yiyecek almamız gerektiğinde, daha fazla para, belki 6 dolar harcamalıyız. Kodumu değiştireceğim, lütfen hazır bilgi 1'e , bununla ilgili soruma odaklanın .

public int getMoneyByPersons(){
    if(persons.size() == 1){ 
        // TODO - return money for one person
    } else {
        // TODO - calculate and return money for people.
    }

}

Arkadaşlarım kodumu gözden geçirmelerini istediğimde, biri değeri 1 için bir ad vermek daha temiz kod vereceğini söyledi, diğeri ise burada sabit bir isme ihtiyacımız olmadığını söyledi çünkü değer tek başına anlamlı.

Yani, sorum şu: 1 değişmez değeri için bir ad vermeli miyim? Değer ne zaman sihirli bir sayıdır ve ne zaman değildir? En iyi çözümü seçmek için bağlamı nasıl ayırt edebilirim?


Nereden personsgeliyor ve ne anlatıyor? Kodunuzun herhangi bir yorumu yok, bu yüzden ne yaptığını tahmin etmek zor.
Hubert Grzeskowiak

7
1'den daha net olmaz. Ama "boyutu" ben "saymak" olarak değiştirir. Ve moneyService olduğu gibi aptalca, kişi koleksiyonuna bağlı olarak ne kadar para iade edileceğine karar verebilmelidir. Bu yüzden insanları getMoney'ye verirdim ve istisnai durumları çözmesine izin verirdim.
Martin Maat

4
Ayrıca, if yan tümcesinizden mantıksal ifadeyi yeni bir yönteme çıkarabilirsiniz. Belki buna IsSinglePerson () deyin. Bu şekilde, sadece bir değişkenten biraz daha fazlasını çıkarırsanız, aynı zamanda if cümlesini biraz daha okunabilir hale
getirirsiniz


2
Belki de bu mantık moneyService.getMoney () 'de daha uygun olur? 1 kişi için getMoney'yi aramanız gereken bir an olur mu? Ama 1'in açık olduğu genel görüşüne katılıyorum. Sihirli sayılar, programcının bu sayıya nasıl geldiğini sorarak kafanızı if(getErrorCode().equals(4095)) ...
Neil

Yanıtlar:


23

Hayır. Bu örnekte, 1 tam anlamıyla anlamlıdır.

Ancak, persons.size () sıfırsa ne olur? persons.getMoney()0 ve 2 için çalışan garip görünüyor ama 1 için değil.


1 'in anlamlı olduğuna katılıyorum. Teşekkürler, bu arada, sorumu güncelledim. Fikrimi almak için tekrar görebilirsiniz.
Jack

3
Yıllar boyunca birçok kez duyduğum bir ifade, sihirli sayıların 0 ve 1 dışındaki isimsiz sabitler olduğudur. Bu sayıların isimlendirilmesi gereken örnekleri potansiyel olarak düşünebiliyorum, ancak saati.
Baldrickk

@Baldrickk Bazen, diğer sayısal değişmezler de adın arkasında gizlenmemelidir.
Deduplicator

@Deduplicator herhangi bir örnek akla geliyor?
Baldrickk

@Baldrickk See amon .
Tekilleştirici

18

Bir kod parçası neden bu belirli değişmez değeri içeriyor?

  • Bu değerin problem alanında özel bir anlamı var mı ?
  • Yoksa bu değer sadece uygulama detayı mıdır, bu değer çevre kodunun doğrudan bir sonucudur?

Değişmez değer bağlamdan net olmayan bir anlama sahipse, evet, bu değere bir sabit veya değişken aracılığıyla bir ad vermek yardımcı olur. Daha sonra, orijinal bağlam unutulduğunda, anlamlı değişken adlarına sahip kod daha sürdürülebilir olacaktır. Unutmayın, kodunuzun hedef kitlesi öncelikle derleyici değildir (derleyici korkunç bir kodla mutlu bir şekilde çalışacaktır), ancak kodun gelecekteki koruyucular - kodun biraz kendi kendini açıkladığını takdir edeceklerdir.

  • İlk örnekte, örneğin değişmezleri anlamı 34, 4, 5bağlamdan açık değildir. Bunun yerine, bu değerlerin bazılarının sorun etki alanınızda özel bir anlamı vardır. Bu nedenle onlara isim vermek güzeldi.

  • İkinci örneğinizde, hazır bilginin anlamı 1bağlamdan çok açıktır. Bir isim vermek yardımcı olmaz.

Aslında, gerçek değerleri gizlediğinden, bariz değerler için ad vermek kötü olabilir.

  • Bu, adlandırılan değer değiştirilirse veya yanlışsa, özellikle de aynı değişken ilişkisiz kod parçalarında yeniden kullanılırsa, hataları gizleyebilir.

  • Bir kod parçası da belirli bir değer için iyi çalışabilir, ancak genel durumda yanlış olabilir. Gereksiz soyutlama getirildiğinde, kod artık doğru değildir.

“Açık” değişmez değerler için boyut sınırı yoktur, çünkü bu tamamen bağlama bağlıdır. Örneğin, değişmez değer 1024, dosya boyutu hesaplaması 31bağlamında veya bir karma işlevi padding: 0.5embağlamında değişmez değer veya bir CSS stil sayfası bağlamında değişmez değer olabilir .


8

Bu kod parçasında, bu arada, bu şekilde kısaltılabilen birkaç sorun vardır:

public List<Money> getMoneyByPersons() {
    return persons.size() == 1 ?
        moneyService.getMoneyIfHasOnePerson() :
        moneyService.getMoney(); 
}
  1. Bir kişinin neden özel bir durum olduğu belirsizdir. Bir kişiden para almanın birkaç kişiden para almanın radikal bir şekilde farklı olduğunu söyleyen özel bir iş kuralı olduğunu düşünüyorum. Ancak, her ikisine de bakmalıyım getMoneyIfHasOnePersonve getMoneyneden farklı vakalar olduğunu anlamayı umuyorum.

  2. İsim getMoneyIfHasOnePersondoğru görünmüyor. Adından, yöntemin tek bir kişi olup olmadığını kontrol etmesini ve bu durumda ondan para almasını beklerim; aksi takdirde hiçbir şey yapmayın. Kodunuzdan bu gerçekleşen şey değildir (ya da koşulu iki kez yapıyorsunuz).

  3. List<Money>Koleksiyondan ziyade geri dönmek için herhangi bir neden var mı ?

Geri sorunuzun, çünkü bir kişi için özel bir tedavi yoktur neden belli değildir, basamaklı bir, bir sabit değiştirilmesi gerektiğini sürece kurallar açık hale getirmek için başka bir yolu yoktur. Burada, kişi diğer sihirli numaralardan çok da farklı değildir. Özel tedavinin bir, iki veya üç kişi veya sadece on iki kişiden fazlası için geçerli olduğunu belirten iş kurallarınız olabilir.

En iyi çözümü seçmek için bağlamı nasıl ayırt edebilirim?

Kodunuzu daha açık hale getiren her şeyi yaparsınız.

örnek 1

Aşağıdaki kod parçasını düşünün:

if (sequence.size() == 0) {
    return null;
}

return this.processSequence(sequence);

Burada sıfır büyülü bir değer midir? Kod oldukça açıktır: dizide hiçbir öğe yoksa, onu işlemeyelim ve özel bir değer döndürelim. Ancak bu kod şu şekilde de yeniden yazılabilir:

if (sequence.isEmpty()) {
    return null;
}

return this.processSequence(sequence);

Burada, artık sabit değil ve kod daha da net.

ÖRNEK 2

Başka bir kod parçası alın:

const result = Math.round(input * 1000) / 1000;

round(value, precision)Aşırı yükü olmayan JavaScript gibi dillerde ne yaptığını anlamak çok fazla zaman almaz .

Şimdi, bir sabiti tanıtmak isterseniz, buna nasıl denir? Bulabileceğiniz en yakın terim Precision. Yani:

const precision = 1000;
const result = Math.round(input * precision) / precision;

Okunabilirliği artırır mı? Olabilir. Burada, bir sabitin değeri oldukça sınırlıdır ve kendinize yeniden düzenlemeyi gerçekten yapmanız gerekip gerekmediğini sorabilirsiniz. Buradaki güzel şey şu ki, hassasiyet sadece bir kez ilan edildi, bu yüzden değişirse, aşağıdaki gibi bir hata yapma riskiniz yoktur:

const result = Math.round(input * 100) / 1000;

bir konumdaki değeri değiştirmek ve diğerinde yapmayı unutmak.

ÖRNEK 3

Bu örneklerden, sayıların her durumda sabitlerle değiştirilmesi gerektiği izlenimini edinebilirsiniz . Bu doğru değil. Bazı durumlarda, sabit olması kodun geliştirilmesine yol açmaz.

Aşağıdaki kod parçasını alın:

class Point
{
    ...
    public void Reset()
    {
        x, y = (0, 0);
    }
}

Sıfırları bir değişkenle değiştirmeye çalışırsanız, zorluk anlamlı bir isim bulmak olacaktır. Nasıl adlandırırdın? ZeroPosition? Base? Default? Burada bir sabitin girilmesi kodu hiçbir şekilde geliştirmez. Biraz daha uzun sürerdi ve sadece bu.

Ancak bu tür vakalar nadirdir. Kodda bir sayı bulduğunuzda, kodun nasıl yeniden düzenlenebileceğini bulmaya çalışın. Kendinize bu sayının bir ticari anlamı olup olmadığını sorun. Evetse, bir sabit zorunludur. Hayır ise, numarayı nasıl adlandırırsınız? Anlamlı bir isim bulursanız, bu harika. Değilse, sabitin gereksiz olduğu bir durum buldunuz.


3a eklerdim: Değişken adlarında para kelimesini kullanmayın, miktar, bakiye veya benzeri kullanın. Bir para toplama mantıklı değil, tutarlar veya bakiyeler koleksiyonu. (Tamam ben yabancı para (ya da daha doğrusu paralar) küçük bir koleksiyon var ama bu farklı bir programlama dışı mesele).
Bent

3

Tek bir parametre alan ve dörde bölünen süreleri beşe bölen bir işlev yapabilir ve bu da ilk örneği kullanırken yaptığı işe temiz bir takma ad sunar.

Sadece kendi normal stratejimi teklif ediyorum ama belki de bir şeyler öğrenirim.

Düşündüğüm şey.

// Just an example name  
function normalize_task_value(task) {  
    return (task * 4) / 5;  // or to `return (task * 4) / NUMBER_OF_TASKS` but it really matters what logic you want to convey and there are reasons to many ways to make this logical. A comment would be the greatest improvement

}  

// Is it possible to just use tasks.length or something like that?  
// this NUMBER_OF_TASKS is the only one thats actually tricky to   
// understand right now how it plays its role.  

function normalize_all_task_values(tasks, accumulator) {  
    for (int i = 0; i < NUMBER_OF_TASKS; i++) {  
        accumulator += normalize_task_value(tasks[i]);
    }
}  

Üssün dışında olduğum için üzgünüm ama sadece bir javascript geliştiricisiyim. Bunu hangi gerekçeyi haklı çıkardığımdan emin değilim, sanırım her şey bir dizi veya listede olmak zorunda değil, ancak .length çok mantıklı olurdu. Ve * 4, kökeni belirsiz olduğundan iyi sabiti yapacaktı.


5
Peki bu 4 ve 5 değerleri ne anlama geliyor? Bunlardan birinin değişmesi gerekiyorsa, numaranın benzer bir bağlamda kullanıldığı tüm yerleri bulabilir ve bu yerleri buna göre güncelleyebilir misiniz? Bu asıl sorunun özüdür.
Bart van Ingen Schenau

Bence ilk örnek oldukça açıklayıcıydı, bu yüzden cevaplayabileceğim biri olmayabilir. Operasyon gelecekte değişmemelidir, ne zaman ihtiyaç duyduğumu başarmak için yeni bir tane yazmam gerektiğinde. Yorumlar bu amaca en çok fayda sağlayacağını düşünüyorum. Kendi dilimde azaltmak için tek satırlık bir çağrı. Ev sahibi için kesinlikle anlaşılabilir kılmak ne kadar önemlidir?
etisdew

@BartvanIngenSchenau Tüm işlev yanlış adlandırılmış. Daha iyi bir işlev adı, işlevin geri dönmesi gereken özellikli bir yorum ve neden * 4 / 5'in bunu başardığını bir yorum tercih ederim. Sabit isimlere gerçekten ihtiyaç duyulmaz.
gnasher729

@ gnasher729: Sabitler, iyi adlandırılmış, iyi belgelenmiş bir işlevin kaynak kodunda tam olarak bir kez görünürse, adlandırılmış bir sabit kullanılması gerekli olmayabilir. Bir sabit, aynı anlama sahip birden çok kez görünür görünmez, bu sabite bir ad vermek, değişmez 42'nin tüm bu örneklerinin aynı şeyi ifade edip etmediğini anlamanıza gerek kalmamasını sağlar.
Bart van Ingen Schenau

Bunun merkezinde evet değerini değiştirmeniz gerekebilir. Ancak, değişkeni temsil etmek için bunu bir sabit olarak değiştirirdim. Durum böyle olduğunu sanmıyorum ve bu sadece yukarıdaki kapsamda yer alan bir ara değişken olmalıdır. Ben koparmak ve her şeyi bir işleve bir parametre yapmak demek istemiyorum ama eğer değişebilir, ancak nadiren ben şu anda bu değişiklik bırakarak yapmak için çalıştığınız o işlev veya nesne / yapı kapsamı için dahili bir parametre yapmak istiyorum sadece küresel kapsam.
etisdew

2

Bu 1 numara, farklı bir numara olabilir mi? 2 veya 3 olabilir mi, yoksa 1 olmasının mantıklı nedenleri var mı? 1 olması gerekiyorsa, 1 kullanılması iyidir. Aksi takdirde, bir sabit tanımlayabilirsiniz. Bu sabit ONE demeyin. (Bunu gördüm).

Dakikada 60 saniye - bir sabite mi ihtiyacınız var? Eh, o ise 60 saniye değil 50 ya da 70. Ve herkes bunu biliyor. Böylece bir numara kalabilir.

Sayfa başına 60 öğe basıldı - bu sayı kolayca 59 veya 55 veya 70 olabilir. Aslında, yazı tipi boyutunu değiştirirseniz, 55 veya 70 olabilir. Burada anlamlı bir sabit daha istenir.

Ayrıca anlamın ne kadar açık olduğu da önemlidir. "Minutes = seconds / 60" yazarsanız, bu açıktır. "X = y / 60" yazarsanız, bu açık değildir. Bir yerde bazı anlamlı isimler olmalı .

Mutlak bir kural vardır: Mutlak kural yoktur. Uygulama ile sayıları ne zaman ve ne zaman adlandırılmış sabitleri kullanacağınızı öğreneceksiniz. Bunu yapma çünkü bir kitap böyle söylüyor - neden böyle söylediğini anlayana kadar.


"Dakikada 60 saniye ... Ve herkes bunu biliyor. Böylece bir numara kalabilir." - Benim için geçerli bir argüman deđil. Kodumda "60" kullanılan 200 yer varsa ne olur? Dakika / saniye cinsinden kullanıldığı yerleri nasıl eşit derecede "doğal" kullanımlarla karşılaştırabilirim? - Bununla birlikte, pratikte ben de minutes*60, bazen hours*3600ek sabitler bildirmeden ihtiyaç duyduğum zamanlarda bile kullanmaya cesaret ediyorum. Günlerce muhtemelen yazacaktım d*24*3600ya d*24*60*60çünkü 86400yakın birisi bir bakışta bu sihirli numarayı tanımayacaktır kenarına etmektir.
JimmyB

@JimmyB Kodunuzdaki 200 yerde "60" kullanıyorsanız, çözümün bir sabit eklemek yerine bir işlevi yeniden düzenlemesi muhtemeldir.
klutt

0

Ben DB alımlarında (değiştirilmiş) OP gibi kod adil bir miktar gördük. Sorgu bir liste döndürür, ancak iş kuralları yalnızca bir öğe olabileceğini söylüyor. Ve sonra, elbette, 'sadece bu tek vaka' için bir şey birden fazla öğe içeren bir listeye dönüştü. (Evet, çok fazla dedim. Neredeyse ... nm gibi)

Bu yüzden yerine bir sabit oluşturmak, (temiz kod yönteminde) bir ad vermek için bir yöntem oluşturmak veya koşullu algılamak ne amaçlıyor (ve onu tespit nasıl kapsül) açıklamak istiyorum:

public int getMoneyByPersons(){
  if(isSingleDepartmentHead()){ 
    // TODO - return money for one person
  } else {
    // TODO - calculate and return money for people.
  }
}

public boolean isSingleDepartmentHead() {
   return persons.size() == 1;
}

Elleçlemeyi birleştirmek için çok tercih edilir. N elemanlarının listesi n ne olursa olsun aynı şekilde ele alınabilir.
Deduplicator

Gördüğüm kadarıyla, tek bir dava, aslında, aynı kullanıcı n-boyut listesinin bir parçası olsaydı, olduğundan farklı (marjinal) bir değer değildi. İyi yapılmış bir OO'da bu bir sorun olmayabilir, ancak eski sistemlerle uğraşırken iyi bir OO uygulaması her zaman mümkün değildir. Elimizdeki en iyi DAO mantık makul bir OO cephe oldu.
Kristian H
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.