Sihirli dizgilerin nesi var?


164

Deneyimli bir yazılım geliştiricisi olarak sihirli iplerden kaçınmayı öğrendim.

Benim sorunum, onları kullanmaya başladığımdan bu yana çok zaman geçtiği için, nedenlerin çoğunu unuttum. Sonuç olarak, daha az deneyimli meslektaşlarım için neden sorun olduklarını açıklamakta güçlük çekiyorum.

Onlardan kaçınmak için hangi nesnel nedenler var? Hangi sorunlara neden olurlar?


38
Sihirli dize nedir? Sihirli sayılarla aynı şey mi?
Laiv,

14
@Laiv: Bunlar sihirli sayılar, en tanımına gibi yes.I benzer konum deviq.com/magic-strings : "Sihirli dizeleri uygulamanın davranışı üzerinde etkisi olan uygulama kodu içinde doğrudan belirtilen dize değerlerdir.". (En tanım en.wikipedia.org/wiki/Magic_string ben hiç aklında ne değildir)
Kramii

17
bu çok komik bir şey demeyi öğrendim ... sonradan Gençlerimi ikna etmek için hangi argümanları kullanabilirim ... Asla bitmeyen hikaye :-). "İkna etmeye" çalışmam, kendi başlarına öğrenmelerine izin vermeyi tercih ederim. Hiçbir şey, kendi deneyiminizin ulaştığı bir dersten / fikirden daha uzun sürmez. Yapmaya çalıştığın şey indoctrinate . Bir Lemmings takımı istemiyorsan bunu yapma.
Laiv

15
@Laiv: İnsanların kendi deneyimlerinden öğrenmelerine izin vermek isterdim, ama ne yazık ki bu benim için bir seçenek değil. Hafif böceklerin hasta bakımını tehlikeye atabileceği ve önlenebilir bakım maliyetlerini karşılayamayacağımız kamuya finanse edilen bir hastanede çalışıyorum.
Kramii

6
@DavidArno, bu soruyu sorarak yaptığı tam olarak yaptığı şey.
user56834

Yanıtlar:


212
  1. Derleyen bir dilde , derleme zamanında bir sihirli dizgenin değeri kontrol edilmez . Dizginin belirli bir desenle eşleşmesi gerekiyorsa, o desene uymasını garanti etmek için programı çalıştırmanız gerekir. Enum gibi bir şey kullandıysanız, derleme zamanında değer yanlış değer olsa bile en azından geçerlidir.

  2. Sihirli bir dize birden fazla yere yazılıyorsa , hepsini güvenlik olmadan (derleme zamanı hatası gibi) değiştirmeniz gerekir. Bu, yalnızca bir yerde bildirilmesi ve değişkenin yeniden kullanılması ile önlenebilir.

  3. Yazım hatası ciddi hatalar olabilir. Eğer bir işlevin varsa:

    func(string foo) {
        if (foo == "bar") {
            // do something
        }
    }
    

    ve yanlışlıkla birileri:

    func("barr");
    

    Daha nadir veya daha karmaşık olan dizgenin daha kötü olması, özellikle projenin ana dilini bilmeyen programcılar varsa.

  4. Sihirli dizeler nadiren kendi kendini belgeliyor. Bir dize görürseniz, bu dize başka ne olabilir / olması gerektiğini söylemez. Doğru ipi seçtiğinizden emin olmak için muhtemelen uygulamaya göz atmanız gerekecektir.

    Bu tür bir uygulama, sızdıran bir şeydir, özellikle karakter mükemmel olması gerektiğinden (3. maddede olduğu gibi) ne yazılması gerektiğini anlamak için dış belgelere ya da koda erişime ihtiyaç duyar.

  5. IDE'lerde "find string" işlevlerinin dışında, kalıbı destekleyen az sayıda araç vardır.

  6. Aynı sihirli ipi iki yerde tesadüfen kullanabilirsiniz, gerçekten farklı şeyler oldukları zaman, eğer bir Bul ve Değiştir yaptıysanız ve her ikisini de değiştirdiyseniz, ikisi de biri çalışırken kırılabilir.


34
İlk argümanla ilgili olarak: TypeScript, dize değişmezlerini yazdırabilecek derlenmiş bir dildir. Bu aynı zamanda iki ila dört argümanı geçersiz kılar. Bu nedenle, dizelerin kendisi sorun değildir, ancak çok fazla değere izin veren bir tür kullanmaktır. Aynı sebep, numaralandırmalar için sihirli tamsayılar kullanmak için de uygulanabilir.
Yogu

11
TypeScript konusunda deneyimim olmadığından, kararınızı erteleyeceğim. O zaman söyleyeceğim şey, denetlenmeyen dizelerin (kullandığım tüm diller için olduğu gibi) sorun olmasıdır.
Erdrik Ironrose,

23
@Yogu Typescript, beklediğiniz statik dizgi değişmez türünü değiştirirseniz, sizin için tüm dizeleri yeniden adlandırmaz. Hepsini bulmanıza yardımcı olmak için derleme zamanı hataları alırsınız, ancak bu sadece 2’de kısmi bir gelişmedir. Kesinlikle şaşırtıcıdan daha az bir şey olduğunu söylememek (çünkü bu ve özelliği seviyorum), ancak kesinlikle düpedüz sayım avantajını ortadan kaldırır. Projemizde, ne zaman ne zaman kullanılacağı ve ne zaman kullanılamayacağımızdan emin olamadığımız bir açık stil sorusu; Her iki yaklaşımın da sıkıntıları ve avantajları vardır.
KRyan

30
Rakamlar için rakamlar kadar değil, ama karakterlerle gerçekleşebilecek büyük bir tane, aynı değere sahip iki sihirli değeriniz olduğu zamandır. Sonra onlardan biri değişir. Artık eski değeri yeni bir değere çevirerek koddan geçiyorsunuz, bu kendi başına işe yarıyor, ancak aynı zamanda yanlış değerleri değiştirmediğinizden emin olmak için EKSTRA işini de yapıyorsunuz. Sabit değişkenlerle, yalnızca manuel olarak geçmek zorunda kalmazsınız, aynı zamanda yanlış olanı değiştirdiğinizden endişelenmezsiniz.
corsiKa

35
@Yogu Ayrıca, bir dize değişmezinin değeri derleme zamanında kontrol ediliyorsa, sihirli bir dize olmaktan çıktığını ileri süreceğim . Bu noktada komik bir şekilde yazılmış olan normal bir const / enum değeridir. Bu bakış açısına göre, yorumunuzun aslında onları reddetmek yerine Erdrik'in puanlarını desteklediğini savunuyorum.
GrandOpener

89

Diğer cevapların neyi kavradığının zirvesi, “sihirli değerlerin” kötü olduğu değil, olmaları gerektiğidir:

  1. tanınabilir olarak sabitler olarak tanımlanır;
  2. tüm kullanım alanları içerisinde yalnızca bir kez tanımlanmış (eğer mümkünse);
  3. Bir şekilde ilişkili bir sabitler dizisi oluştururlarsa birlikte tanımlanırlar;
  4. kullanıldığı uygulamada uygun bir genellikte tanımlanmış; ve
  5. uygunsuz bağlamlarda kullanımlarını sınırlayacak şekilde tanımlanır (örn. kontrol tipine uygun).

Tipik olarak kabul edilebilir "sabitleri" "sihirli değerlerden" ayıran şey, bu kuralların bir veya daha fazlasının ihlalidir.

İyi kullanıldığında, sabitler kodumuzun belirli aksiyomlarını ifade etmemize izin verir.

Bu da beni son bir noktaya getiriyor; yukarıdaki kriterlere uymasa bile, sabit değerlerin aşırı kullanımı (ve dolayısıyla değerler açısından ifade edilen aşırı sayıda varsayım ya da kısıtlama), (özellikle onlardan sapması durumunda), Bu, tasarlanan çözümün yeterince genel veya iyi yapılandırılmamış olduğu anlamına gelebilir (bu nedenle artık sabitlerin artıları ve eksileri hakkında değil, iyi yapılandırılmış kodun artıları ve eksileri hakkında gerçekten konuşmuyoruz).

Yüksek seviyeli diller, sabit seviyelerde çalışmak zorunda kalacak olan düşük seviyeli dillerdeki kalıplar için yapılara sahiptir. Aynı paternler üst seviye bir dilde de kullanılabilir, ancak kullanılmaması gerekir.

Ancak bu, tüm koşulların izlenimine ve bir çözümün neye benzemesi gerektiğine ve bu kararın tam olarak nasıl haklı çıkacağına dayanan bir uzman kararı olabilir. Gerçekten de, "Aşina olduğum, daha iyi yaptığım bu tür işleri görecek kadar yaşlandım" iddiası dışında, genel ilkeler açısından haklı bulunmayabilir!

EDIT: bir düzenlemeyi kabul etmiş, bir başkasını reddetmiş ve şimdi kendi düzenlememi yaptım, şimdi bir kez ve tüm haha ​​için çözülecek olan kural listemin biçimlendirme ve noktalama stilini düşünebilir miyim?


2
Bu cevabı beğendim. Sonuçta "struct" (ve diğer tüm ayrılmış kelimeler) C derleyicisine sihirli bir dizedir. Onlar için kodlamanın iyi ve kötü yolları vardır.
Alfred Armstrong

6
Örnek olarak, eğer biri kodunuzda “X: = 898755167 * Z” yi görürse, muhtemelen ne anlama geldiğini ve hatta yanlış olduğunu bilme ihtimalinin düşük olduğunu bilemez. Ama eğer “Speed_of_Light: constant Tamsayı: = 299792456” diye görürlerse birisi onu arayacak ve doğru değeri önerecektir (ve hatta daha iyi bir veri türü).
WGroleau

26
Bazı insanlar noktayı tamamen özlüyor ve SEPARATOR = "," yerine COMMA = "," yazıyor. İlki daha net bir şey yapmaz, ikincisi amaçlanan kullanımı belirtir ve daha sonra tek bir yerde ayırıcıyı değiştirmenize izin verir.
marcus

1
@ marcus, gerçekten! Elbette yerinde basit değişmez değerleri kullanmak için bir durum söz konusudur - örneğin, bir yöntem bir değeri ikiye bölerse , başka yerde tanımlanmış value / 2olandan ziyade, basitçe yazmak daha net ve basit olabilir . CSV'leri işleyen bir yöntemi genelleştirmek istiyorsanız, ayırıcının muhtemelen parametre olarak tanımlanmasını ve hiç bir sabit olarak tanımlanmamasını istersiniz. Fakat hepsi bağlamda bir yargı meselesidir - @ WGroleau'nun örneği, açıkça adlandırmak isteyeceğiniz bir şeydir, ancak her kelimenin tam anlamıyla buna ihtiyacı yoktur. value / VALUE_DIVISOR2SPEED_OF_LIGHT
Steve,

4
En iyi cevap, sihirli dizgilerin "kötü bir şey" olduğuna ikna edilmesi gerekiyorsa, bu cevaptan daha iyidir. Bu cevap, onların "kötü bir şey" olduğunu biliyor ve kabul ediyorsanız ve hizmet ettikleri ihtiyaçları sürdürülebilir bir şekilde yerine getirmenin en iyi yolunu bulmanız gerekiyorsa daha iyidir.
corsiKa

34
  • İzlemeleri zor.
  • Tümünü değiştirmek, muhtemelen birden fazla projede birden fazla dosya değiştirmeyi gerektirebilir (bakımı zor).
  • Bazen amaçlarının ne olduğunu sadece değerlerine bakarak söylemek zor.
  • Yeniden kullanım yok.

4
"Yeniden kullanım yok" ne demek?
Güle güle,

7
Bir değişken / sabit vb. Oluşturmak yerine tüm projeniz / kodunuzda tekrar kullanmak yerine, her birinde bir gereksiz çoğaltmaya neden olan yeni bir dize oluşturursunuz.
Jason,

Yani 2. ve 4. nokta aynı mı?
Thomas,

4
Hayır o şekilde bahsediyor @ThomasMoors yeni bir dize, bir kullanmak istediğiniz her zaman oluşturmak zorunda mevcut sihirli dize noktası 2 dize üzerinde değişiklik hakkında
Pierre Arlaud

25

Gerçek hayattan örnek: "Varlıklar" ın "alanlar" ile depolandığı üçüncü parti bir sistemle çalışıyorum. Temel olarak bir EAV sistemi. Başka bir alan eklemek oldukça kolay olduğundan, alanın adını dize olarak kullanarak birine erişebilirsiniz:

Field nameField = myEntity.GetField("ProductName");

("ProductName" sihirli dizesine dikkat edin)

Bu birkaç soruna yol açabilir:

  • "ProductName" in bile bulunduğunu ve tam olarak yazımını bilmek için harici belgelere başvurmam gerekiyor
  • Artı, o alanın veri tipinin ne olduğunu görmek için o doktora başvurmam gerekiyor.
  • Bu sihirli dizede yazım hatası, bu kod satırı yürütülene kadar yakalanmaz.
  • Birisi bu alanı sunucuda yeniden adlandırmaya karar verdiğinde (veri kaybını önlerken zor, ancak imkansız değildir), o zaman bu adı nerede ayarlamam gerektiğini görmek için kodumla kolayca arama yapamıyorum.

Bu yüzden benim çözümüm, varlık türüne göre düzenlenen bu isimler için sabitler üretmekti. Yani şimdi kullanabilirim:

Field nameField = myEntity.GetField(Model.Product.ProductName);

Hala bir dize sabittir ve aynı ikili dosyaya derlenir, ancak birkaç avantajı vardır:

  • "Model" yazdıktan sonra, IDE sadece mevcut varlık türlerini gösterir, bu yüzden kolayca "Ürün" ü seçebilirim.
  • O zaman IDE'm sadece bu tür bir varlık için uygun olan ve aynı zamanda seçilebilir olan alan adlarını sağlar.
  • Otomatik olarak oluşturulan belgeler bu alanın ne anlama geldiğini ve değerlerini saklamak için kullanılan veri türünü gösterir.
  • Sabitten başlayarak, IDE'm bu sabitin kullanıldığı tüm yerleri bulabilir (değerinin aksine)
  • Yazım hatası derleyici tarafından yakalanacak. Bu, sabitleri yeniden oluşturmak için yeni bir model (muhtemelen bir alanı yeniden adlandırdıktan veya sildikten sonra) kullanıldığında da geçerlidir.

Listemde sonra: bu sabitleri kuvvetle yazılan sınıfların arkasına gizleyin - ardından veri türü de güvence altına alındı.


+1 kod yapısıyla sınırlı olmayan birçok iyi noktaya
sahipsiniz

Varlık türünüzün bazı bölümleri aslında sabit bir ad tanımlamaya değecek kadar statikse, bunun için sadece uygun bir veri modeli tanımlamanın daha uygun olacağını düşünüyorum nameField = myEntity.ProductName;.
Yalan Ryan

@ LieRyan - düz sabitler oluşturmak ve bunları kullanmak için mevcut projeleri yükseltmek çok daha kolaydı. Yani dedim am statik türlerini üreten çalışan yüzden tam olarak yapmak olabilir
Hans Ke st ing

9

Sihirli teller her zaman kötü değil , bu yüzden onlardan kaçınmak için bir battaniye nedeni bulamamanızın nedeni olabilir. ("Sihirli string" ile demek istediğim, bir ifadenin parçası olarak değişmez ve string olarak tanımlanmamış).

Bazı özel durumlarda, sihirli dizgilerden kaçınılmalıdır:

  • Aynı dize, kodda birden çok kez görüntüleniyor. Bu, yerlerden birinde bir yazım hatası olabilir anlamına gelir. Ve dize değişikliklerinin bir güçlüğü olacak. Dize bir sabit haline getirin, bu sorundan kaçınırsınız.
  • Dize göründüğü koddan bağımsız olarak değişebilir. Örneğin. dizge son kullanıcıya görüntülenen metin ise, herhangi bir mantık değişikliğinden bağımsız olarak değişecektir. Bu dizgiyi ayrı bir modüle (veya harici konfigürasyona veya veritabanına) ayırmak, bağımsız olarak değiştirmeyi kolaylaştıracak
  • Dizenin anlamı, bağlamdan açık değildir. Bu durumda bir sabit tanıtmak kodun anlaşılmasını kolaylaştıracaktır.

Ancak bazı durumlarda, "sihirli dizeler" iyi. Basit bir ayrıştırıcınız olduğunu söyleyin:

switch (token.Text) {
  case "+":
    return a + b;
  case "-":
    return a - b;
  //etc.
}

Burada gerçekten sihir yok ve yukarıda açıklanan sorunlardan hiçbiri geçerli değil. IMHO'nun tanımlanması string Plus="+"vb. Yararı olmayacaktı . Basit tutun.


7
Bence "sihirli ip" tanımınız yetersiz, gizleme / gizleme / gizleme yapma kavramına sahip olması gerekiyor. Bu karşı örnekteki "+" ve "-" ifadelerini "sihir" olarak tanımlamam, sıfır olarak sihir olarak nitelendirmemden daha fazla bahsetmem if (dx != 0) { grad = dy/dx; }.
Rupé

2
@Rupe: Katılıyorum, ancak OP , dizenin gizemli olmasını gerektirmeyen " uygulamanın davranışı üzerinde doğrudan etkisi olan uygulama kodunda belirtilen dize değerleri " tanımını kullanıyor. cevap.
JacquesB

7
Senin örneğe atıfla, ben yerini anahtar ifadeleri gördük "+"ve "-"birlikte TOKEN_PLUSve TOKEN_MINUS. Her okuduğumda bundan dolayı okumak ve hata ayıklamak zor oldu! Kesinlikle basit dizeleri kullanmanın daha iyi olduğu konusunda hemfikir olduğum bir yer.
Cort Ammon

2
Sihirli dizelerin uygun olduğu zamanlar olduğu konusunda hemfikirim: onlardan kaçınmak bir kuraldır ve tüm kuralların istisnaları vardır. Umarım, neden kötü bir şey olabileceği konusunda net olduğumuzda, bazı şeyleri yapmak yerine akıllı seçimler yapabileceğiz çünkü (1) daha iyi bir yol olabileceğini asla anlamadık, veya (2) üst düzey bir geliştirici veya kodlama standardı tarafından farklı şeyler yapılması söylendi.
Kramii

2
Burada "büyü" nin ne olduğunu bilmiyorum. Bunlar bana basit harflerle yazılmış gibi görünüyor.
tchrist

6

Mevcut cevaplara eklemek için:

Uluslararasılaşma (i18n)

Ekranda görüntülenecek metin kodlanmış ve işlev katmanlarına gömülmüşse, o metnin diğer dillere çevirisini sağlamak için çok zor bir zaman geçireceksiniz.

Bazı geliştirme ortamları (örneğin Qt), temel dil metin dizesinden çevrilen dile kadar arama yaparak çevirileri işler. Sihirli dizgiler genellikle hayatta kalabilir - aynı metni başka bir yerde kullanmak ve bir yazım hatası almak istediğinize karar verene kadar. O zaman bile, başka bir dile destek eklemek istediğinizde hangi sihirli dizgelerin çevrilmesi gerektiğini bulmak çok zor.

Bazı geliştirme ortamları (örneğin, MS Visual Studio) başka bir yaklaşıma sahiptir ve tüm çevrilmiş dizgilerin bir kaynaklar veritabanında tutulmasını ve bu dizinin benzersiz kimliği ile mevcut yerel ayar için tekrar okunmasını gerektirir. Bu durumda, sihirli dizelerle uygulamanız, basit bir şekilde yeniden işleme tabi tutulmadan başka bir dile çevrilemez. Etkili geliştirme, tüm metin dizelerinin kaynaklar veritabanına girilmesini ve kod ilk yazıldığında benzersiz bir kimlik verilmesini gerektirir ve bundan sonra i18n oldukça kolaydır. Gerçek şu ki bunu doldurmaya çalışmak genellikle çok büyük bir çaba gerektirecektir (ve evet, orada oldum!), Bu yüzden her şeyi en başta yapmak çok daha iyidir.


3

Bu herkes için bir öncelik değil, ancak kodunuzdaki kaplin / uyum ölçütlerini otomatik olarak hesaplamak istiyorsanız, sihirli dizeler bunu neredeyse imkansız hale getirir. Bir yerdeki bir dize, başka bir yerdeki bir sınıfa, yönteme veya işleve atıfta bulunur ve dizenin yalnızca kodu ayrıştırma yaparak sınıfa / yönteme / işleve bağlandığını belirlemenin kolay ve otomatik bir yolu yoktur. Sadece temel çerçevenin (Açısal, örneğin) bir bağlantı olduğunu belirleyebilir - ve bunu sadece çalışma zamanında yapabilir. Eşleştirme bilgisini kendiniz elde etmek için, çözümleyiciniz, kullandığınız çerçeve hakkında, kodladığınız temel dilin üstünde ve ötesinde her şeyi bilmek zorundadır.

Fakat yine de, bu geliştiricilerin çok ilgilendiği bir şey değil.

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.