Sihirli Sayıları Silme: “Hayır” deme zamanı ne zaman?


36

Sihirli numaraların (kodlanmış değerler) programınızda, özellikle de yorum içermeyen bir kod bölümünü değiştirme zamanı geldiğinde, hasara yol açabileceğinin farkındayız, ancak çizgiyi nereye çiziyorsunuz?

Örneğin, iki gün arasındaki saniye sayısını hesaplayan bir işleve sahipseniz,

seconds = num_days * 24 * 60 * 60

ile

seconds = num_days * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE

Hangi noktada, kodlanmış değerin ne anlama geldiği ve onu yalnız bırakacağının tamamen açık olduğuna karar veriyorsunuz?


2
Neden gibi bakıyor kadar kodunuz biter böylece bir fonksiyonu veya makro ile bu hesaplamayı yeriniseconds = CALC_SECONDS(num_days);
FrustratedWithFormsDesigner

15
TimeSpan.FromDays(numDays).Seconds;
Hiç kimse

18
@oosterwal: Bu tavırla ( HOURS_PER_DAY will never need to be altered), Mars'ta konuşlandırılmış yazılımları asla kodlamayacaksınız. : P
SinirliFormsDesigner ile

23
Sabit sayısını sadece SECONDS_PER_DAY = 86400'e düşürürdüm. Değişmeyecek bir şeyi neden hesaplıyorsun?
JohnFx

17
Artık saniye ne olacak?
John

Yanıtlar:


40

Sayısal değişmezler yerine sembolik sabitleri kullanmanın iki nedeni vardır:

  1. Sihirli sayılar değişirse bakımı basitleştirmek için. Bu, örneğiniz için geçerli değildir. Bir saatteki saniye sayısının veya bir gündeki saat sayısının değişmesi son derece düşük bir ihtimal.

  2. Okunabilirliği arttırmak için. "24 * 60 * 60" ifadesi hemen hemen herkes için açıktır. "SECONDS_PER_DAY" de öyle, ancak bir hata avlıyorsanız, SECONDS_PER_DAY'ın doğru tanımlandığını kontrol etmeniz gerekebilir. Kısalık içinde değer var.

Tam olarak bir kez görünen ve programın geri kalanından bağımsız olan sihirli sayılar için, bu sayı için bir sembol oluşturulup oluşturulmayacağına karar vermek bir zevk meselesidir. Herhangi bir şüpheniz varsa, devam edin ve bir sembol oluşturun.

Bunu yapma:

public static final int THREE = 3;

3
+1 @kevin cline: Kısalık avlama konusunda kıskançlık noktanıza katılıyorum. Özellikle bir hata ayıklama sırasında adlandırılmış bir sabit kullandığımı gösteren ek yarar, bir sabitin yanlış tanımlandığı tespit edilirse, yanlış uygulanan tüm oluşumlar için tüm bir projeyi aramak yerine yalnızca bir kod parçasını değiştirmeniz gerekmesidir. değer.
oosterwal

40
Daha da kötüsü:publid final int FOUR = 3;
gablin

3
Ah canım, bir zamanlar birlikte çalıştığım aynı adamla çalışmış olmalısın.
hızla_10

2
@gablin: Adil olmak gerekirse, pubların kapakları olması oldukça yararlı.
Alan Pearce

10
Bunu gördüm: public static int THREE = 3;... not - hayır final!
Stephen C

29

Asla sihirli numaralara sahip olmama kuralını korurum.

Süre

seconds = num_days * 24 * 60 * 60

Çatırtı modunda üç veya dört hafta boyunca günde 10 saat boyunca kodlandıktan sonra çoğu zaman mükemmel şekilde okunabilir

seconds = num_days * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE

okumak çok daha kolaydır.

Hayal kırıklığına uğramışFormsDesigner'ın önerisi daha iyi:

seconds = num_days * DAYS_TO_SECOND_FACTOR

hatta daha da iyisi

seconds = CONVERT_DAYS_TO_SECONDS(num_days)

Çok yorgun olduğunuzda işler açık kalmaz. Defansif kodlayın .


13
Tanımladığınız gibi bir crunch moduna girmek, kaçınılması gereken zahmetli bir antipternürdür. Programcılar haftada yaklaşık 35-40 saatte en yüksek sürdürülebilir üretkenliğe ulaşırlar.
btilly

4
@btilly Bütün kalbimle seninle aynı fikirdeyim. Ancak, genellikle dış faktörlerden dolayı olur.
Vitor Py

3
Genel olarak ikinci, dakika, gün ve saat için sabitleri tanımlar. '30 * MINUTE 'başka bir şey okumak gerçekten kolay değilse ve bunun hakkında düşünmek zorunda olmadan bir zaman biliyorum.
Zachary K

9
@btilly: Tepe 35-40 saat veya% 0.129 ile% 0.138 arasında bir kan alkol seviyesidir. Ben okudum XKCD doğru olmalı, böylece!
oosterwal

1
HOURS_PER_DAY gibi bir sabit görseydim, onu silerdim ve sonra sizi arkadaşlarınızın önünde küçük düşürürüm. Tamam, belki halkın aşağılanmasından vazgeçerdim ama muhtemelen silerdim.
Ed S.

8

Hayır deme zamanı neredeyse her zaman. Sadece kodlanmış sayıları kullanmanın daha kolay olduğunu düşündüğüm zamanlar UI düzeni gibi yerlerdedir - form üzerindeki her kontrolün konumlandırılması için bir sabit oluşturmak çok kübik ve yorucu hale gelir ve bu kod genellikle UI tasarımcısı tarafından kullanılırsa önemli değil. ... kullanıcı arayüzü dinamik olarak belirtilmediği veya bir çapaya göreceli pozisyonları kullanmadığı veya elle yazılmadığı sürece. Bu durumda, mizanpaj için bazı anlamlı sabitler tanımlamanın daha iyi olacağını söyleyebilirim. Ve burada ya da oradaki bir şekerleme faktörüne "doğru" bir şeyi hizalamak / konumlandırmak için ihtiyacınız varsa, bunun da tanımlanması gerekir.

Ama örnekte, ben yerine sizce 24 * 60 * 60tarafından DAYS_TO_SECONDS_FACTORdaha iyidir.


Bağlam ve kullanım tamamen açık olduğunda kodlanmış değerlerin de iyi olduğunu kabul ediyorum. Ancak bu, bir yargılama çağrısıdır ...

Örnek:

@Rmx'in işaret ettiği gibi, bir listenin boş olup olmadığını kontrol etmek için 0 veya 1 kullanmak, belki de bir döngünün sınırları içerisinde sabitin amacının çok açık olduğu bir durumun bir örneğidir.


2
Kullanmak genelde sorun olmaz, 0ya da 1sanırım. if(someList.Count != 0) ...daha iyidir if(someList.Count != MinListCount) .... Her zaman değil ama genel olarak.
Hiç kimse

2
@Dima: VS tasarımcının hepsini ele alıyor. Eğer sabitler oluşturmak istiyorsa, bu benim için sorun değil. Ancak oluşturulan koda girmiyorum ve tüm kodlanmış değerleri sabitlerle değiştiriyorum.
SinirliFormsDesigner ile

4
Yine de insan tüketimi için yazılmış bir araç koduyla üretilmiş ve kullanılan kod ile karıştırmayınız.
biziclop

1
@FustustratedWithFormsDesigner @biziclop'ın işaret ettiği gibi, üretilen kod tamamen farklı bir hayvandır. Adlandırılmış sabitler kesinlikle insanlar tarafından okunan ve değiştirilen kodda kullanılmalıdır. Üretilen kod, en azından ideal durumda, hiç değiştirilmemelidir.
Dima,

2
@FrustratedWithFormsDesigner: Programınızda aniden değiştirilmesi gereken düzinelerce dosyada kodlanmış iyi bilinen bir değere sahipseniz ne olur? Örneğin, gömülü bir işlemci için mikrosaniye başına saat tik sayısını temsil eden değeri sabit olarak kodlarsanız, ardından yazılımınızı mikrosaniye başına farklı sayıda saat tikinin olduğu bir tasarıma taşımanız söylenir. Değeriniz 8 gibi ortak bir şey olsaydı, düzinelerce dosyada bul / değiştir işlevinin kullanılması daha fazla soruna neden olabilirdi.
oosterwal

8

Numaraya bir anlam veya amaç saptayamazsanız durun.

seconds = num_days * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE

okumak sadece sayıları kullanmaktan daha kolaydır. (Tek bir SECONDS_PER_DAYsabit alarak daha okunabilir hale getirilebilse de , bu tamamen ayrı bir konudur.)

Koduna bakarak bir geliştiricinin ne yaptığını görebileceğini varsayalım. Ama nedenini de bildiklerini varsaymayın. Sabitiniz nedenini anlamaya yardımcı oluyorsa, bunun için gidin. Eğer değilse, yapma.

Tek bir cevapla önerildiği gibi çok fazla sabitle karşılaşırsanız, bir dosyada onlarca sabit olması okunabilirliği tam olarak iyileştirmediğinden, bunun yerine harici bir yapılandırma dosyası kullanmayı düşünün.


7

Muhtemelen şöyle şeylere "hayır" derdim:

#define HTML_END_TAG "</html>"

Ve kesinlikle "hayır" derdi ki:

#define QUADRATIC_DISCRIMINANT_COEF 4
#define QUADRATIC_DENOMINATOR_COEF  2

7

Sabitlerin kullanımını açık bir şekilde desteklemek için bulduğum en iyi örneklerden biri HOURS_PER_DAYşudur:

Bir kişinin iş kuyruğunda ne kadar süre oturduğunu hesaplıyorduk. Gereksinimler gevşek bir şekilde tanımlandı ve programlayıcı 24birkaç yerde kodlandı . Sonunda, gerçekten günde sadece 8 saat çalıştığında, 24 saat boyunca kullanıcıları bir sorunla karşı karşıya kalmaları için cezalandırmanın adil olmadığını fark ettik. Görev bunu düzeltmeye VE diğer raporların aynı sorunu yaşamaya başladığını görünce, 24 kodla grep / arama yapmak zordu, grep / arama yapmak çok daha kolay olurduHOURS_PER_DAY


ayetler, bu nedenle günlük saatler, günlük çalışma saatlerine (7.5 BTW çalışıyorum) ya da günde saatlere bakmanıza göre değişir. Bunun gibi sabitin anlamını değiştirmek için, ismini başka bir şeyle değiştirmek istersiniz. Arama yapmak sizin için kolaylaştıracak nokta olsa da geçerlidir.
gbjbaanb

2
Öyle görünüyor ki bu durumda HOURS_PER_DAY de gerçekten istenen sabit değildi. Ancak, ismini arayarak araştırabilmeniz büyük bir faydadır, hatta (ya da özellikle de) birçok yerde başka bir şeye değiştirmeniz gerekse bile .
David K,

4

Sayının tamamen sabit olduğu ve değişme ihtimalinin olmadığı sürece, tamamen kabul edilebilir olduğunu düşünüyorum. Öyleyse, sizin durumunuzda seconds = num_days * 24 * 60 * 60iyi (tabii ki bir döngü içinde bu tür bir hesaplama yapmak gibi aptalca bir şey yapmadığınızı varsayarsak) ve okunabilirlik açısından tartışmasız daha iyi seconds = num_days * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE.

Böyle kötü şeyler yaptığın zaman:

lineOffset += 24; // 24 lines to a page

Sayfadaki satırlara daha fazla sığamasanız veya değiştirmeye niyetli olmasanız bile, bunun yerine sabit bir değişken kullanın, çünkü bir gün size musallat gelecektir. Sonuçta, nokta okunabilirliktir, CPU'da 2 hesaplama çevrimi tasarrufu olmaz. Bu, artık 1978'de değerli baytların tüm değerleri için sıkıldığı bir zaman değil.


2
Adı belirtilen SECONDS_PER_DAY sabitini kullanmak yerine, değişmeyen değeri 86400 olarak kodlamanın kabul edilebilir olduğunu düşünüyor musunuz? Örneğin, değerin tüm oluşumlarının doğru olduğunu ve 0 eksik olmadığını veya 6 reklamı 4 değiştirdiğini nasıl doğrularsınız?
oosterwal

Öyleyse neden olmasın: saniye = num_days * 86400? Bu da değişmeyecek.
JeffO

2
SECONDS_PER_DAY işini iki yoldan da yaptım - adı kullanarak ve numarayı kullanarak. 2 yıl sonra koda geri döndüğünüzde, HER ZAMAN adlandırılmış sayı HER ZAMAN daha mantıklıdır.
hızla_10

2
saniye = num_days * 86400 bana göre net değil. Sonunda önemli olan budur. Eğer "saniye = num_days * 24 * 60 * 60" görürsem, değişken isimlerinin bu örnekte anlamı oldukça iyi bir şekilde vermesi gerçeği dışında, hemen kendime bunları neden ayırdığımı ve anlamın belirgin hale geldiğini açıkladım. sayılar olarak (dolayısıyla sabittirler), daha fazla araştırmanın değerleri anlamamı gerektirecek değişkenleri değil, sürekli olup olmadıklarını gösterirler.
Neil

1
İnsanların sık sık fark etmediği şeyler: Eğer bu lineOffset değerini 24'ten 25'e değiştirirseniz, 24'ün nerede kullanıldığını ve değişmesi gerekip gerekmediğini görmek için tüm kodunuzu gözden geçirmeniz ve ardından tüm günler ila saat hesaplamaları yapmanız gerekir. 24'le çarpmak gerçekten de senin yoluna girmek
gnasher729

3
seconds = num_days * 24 * 60 * 60

Tamamen iyi. Bunlar asla değişmeyeceklerinden, aslında sihirli sayılar değildir.

Makul bir şekilde değişebilen veya belirgin bir anlamı olmayan numaralar değişkenlere konulmalıdır. Bu hemen hemen hepsi anlamına gelir.


2
Misiniz seconds = num_days * 86400hala kabul edilebilir? Bunun gibi bir değer birçok farklı dosyada birden çok kez kullanılmışsa, birinin yanlışlıkla seconds = num_days * 84600bir veya iki yere girmediğini nasıl doğrularsınız ?
oosterwal

1
86400 yazmak 24 * 60 * 60 yazmaktan çok farklıdır.
Carra

4
Tabii ki değişecek. Her gün içinde 86.400 saniye yok. Örneğin, gün ışığından yararlanma saati düşünün. Yılda bir kez, bazı yerlerde günde sadece 23 saat, başka bir günde ise 25'er yaşarlar .
Dave DeLong

1
@Dave, mükemmel nokta. Leap saniye var - en.wikipedia.org/wiki/Leap_second

Doğru tespit. Bu istisnaları yakalamanız gerektiğinde bir işlev eklemek bir güvenlik önlemidir.
Carra

3

Bir değeri bir birimden diğerine dönüştürmek için sabitler (sihirli değerler) oluşturmaktan kaçınırdım. Dönüştürme durumunda bir konuşma yöntemi adı tercih ederim. Bu örnekte bu, örneğin DayToSeconds(num_days)içsel olacaktır , çünkü yöntemin sihirli değerlere ihtiyacı yoktur, çünkü "24" ve "60" ın anlamı açıktır.

Bu durumda asla saniye / dakika / saat kullanmam. Sadece TimeSpan / DateTime kullanırdım.


1

Karar vermek için bağlamı parametre olarak kullanma

Örneğin, "calculatorlateSecondsBetween: aDay ve: anotherDay" adlı bir işleve sahipsiniz, bu ismin ne olduğu hakkında çok fazla açıklama yapmanız gerekmeyecek çünkü işlev ismi oldukça temsilidir.

Ve bir başka soru da, farklı bir şekilde hesaplama olanakları hangileri? Bazen aynı şeyi yapmanın pek çok yolu vardır, bu nedenle gelecekteki programcılara rehberlik etmek ve onlara hangi yöntemi kullandığınızı göstermek için, sabitleri tanımlamanın sorunu çözmesine yardımcı olabilir.

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.