“Değişkenler mümkün olan en küçük kapsamda yaşamalıdır”, “Mümkünse değişkenler olmamalı” durumunu içeriyor mu?


32

" Örnek değişkenleri yerine yerel değişkenleri tercih etmenin gerekçesi mi? " Üzerine kabul edilen cevaba göre değişkenler mümkün olan en küçük kapsamda yaşamalıdır.
Sorunu yorumuma göre basitleştirin, bu tür bir kodu yeniden gözden geçirmemiz gerektiği anlamına gelir:

public class Main {
    private A a;
    private B b;

    public ABResult getResult() {
        getA();
        getB();
        return ABFactory.mix(a, b);
    }

    private getA() {
      a = SomeFactory.getA();
    }

    private getB() {
      b = SomeFactory.getB();
    }
}

Bunun gibi bir şeye:

public class Main {
    public ABResult getResult() {
        A a = getA();
        B b = getB();
        return ABFactory.mix(a, b);
    }

    private getA() {
      return SomeFactory.getA();
    }

    private getB() {
      return SomeFactory.getB();
    }
}

ama "ruhu" na göre "değişkenler mümkün olan en küçük kapsamda yaşamalıdır", "asla değişkenlere sahip değil" "değişkenlere sahip" den daha küçük kapsamlara sahip değil mi? Bu nedenle yukarıdaki sürümün yeniden yapılandırılması gerektiğini düşünüyorum:

public class Main {
    public ABResult getResult() {
        return ABFactory.mix(getA(), getB());
    }

    private getA() {
      return SomeFactory.getA();
    }

    private getB() {
      return SomeFactory.getB();
    }
}

bu yüzden getResult()hiçbir yerel değişken yok. Bu doğru mu?


72
Açık değişkenler oluşturmak, bunları adlandırmak zorunda kalmanın yararı ile birlikte gelir. Birkaç değişkene giriş yapmak, opak bir yöntemi hızlı bir şekilde okunabilir hale getirebilir.
Jared Goguen

11
Dürüst olmak gerekirse, a ve b değişken isimleri cinsinden bir örnek, yerel değişkenlerin değerini gösterememek için çok fazladır. Neyse ki, yine de iyi cevaplar aldın.
Doktor Brown,

3
İkinci snippet'inizde değişkenleriniz gerçekten değişken değil . Onlar etkili bir şekilde yerel sabitlerdir, çünkü onları değiştirmezsiniz. Java derleyici, eğer nihai olarak tanımladıysanız onlara yerel olarak davranırlar ve derleyici onlarla ne olacağını bilir . Gerçekten finalanahtar kelime kullanıp kullanmamalısınız, bir stil ve görüş meselesidir .
hyde

2
@JaredGoguen Açık değişkenler oluşturmak, bunları adlandırmak zorunda kalmanın yükü ve bunları adlandırmanın yararı ile birlikte gelir.
Bergi

2
"Değişkenler mümkün olan en küçük kapsamda yaşamalı" gibi kesinlikle kod stili kurallarının sıfırı evrensel olarak düşünülmeden uygulanabilir. Bu nedenle, asla basit bir kılavuz aldığınız ve daha az açık bir şekilde ele alınacak bir alana genişlettiğiniz (bu nedenle değişkenlerin mümkünse küçük kapsamları olmalıdır) "mümkünse değişkenler olmamalıdır"). Sonuç olarak, özellikle sonucunuzu destekleyen iyi sebepler var ya da sonucunuz uygun olmayan bir uzantısı; Her iki şekilde de orijinal kılavuza ihtiyacınız yoktur.
Ben

Yanıtlar:


110

Hayır. Bunun birkaç nedeni var:

  1. Anlamlı adlara sahip değişkenler kodun daha kolay anlaşılmasını sağlayabilir.
  2. Karmaşık formülleri daha küçük adımlara bölmek kodun okunmasını kolaylaştırabilir.
  3. Caching.
  4. Bir kereden fazla kullanılabilecek şekilde nesnelere referanslar tutmak.

Ve bunun gibi.


22
Ayrıca bahsetmeye değer: Değer ne olursa olsun bellekte saklanacak, bu yüzden yine de aynı kapsamda bitiyor. Adını da söyleyin (Robert yukarıda bahsettiği nedenlerden dolayı)!
Maybe_Factor

5
@Maybe_Factor Kılavuz, performans nedenleriyle gerçekten orada değil; Bu, kodu korumanın ne kadar kolay olduğu ile ilgilidir. Ancak bu, yalnızca iyi adlandırılmış ve tasarlanmış işlevler (örneğin, gerekçe yok var taxIndex = getTaxIndex();) oluşturmadığınız sürece, anlamlı yerlilerin büyük bir ifadeden daha iyi olduğu anlamına gelir .
Luaan

16
Hepsi önemli faktörlerdir. Ona bakış şekli: kısalık bir hedeftir; netlik başkadır; sağlamlık başkadır; performans başka; ve bunun gibi. Bunların hiçbiri mutlak hedefler değildir ve bazen çatışırlar. Bizim işimiz bu hedefleri en iyi şekilde nasıl dengeleyeceğimizi bulmak .
Mart’ta

8
Derleyici 3 & 4 ile ilgilenecek
16'da Monica

9
4 numara doğruluk için önemli olabilir. Bir işlev çağrısının değeri değişebilirse (örn now().) Değişkeni kaldırmak ve yöntemi bir kereden fazla çağırmak hatalara yol açabilir. Bu, gerçekten ince ve hata ayıklaması zor olan bir durum yaratabilir. Belli gözükebilir ama değişkenleri kaldırmak için kör bir yeniden düzenleme görevindeyseniz, kusurları ortaya çıkarmak son derece kolaydır.
JimmyJames

16

Kabul, gerekli olmayan ve kodun okunabilirliğini geliştirmeyen değişkenlerden kaçınılmalıdır. Kodda belirli bir noktada kapsamda olan değişkenler ne kadar fazlaysa kod o kadar karmaşıktır.

Değişkenlerin faydasını gerçekten görmüyorum ave börneğinizde, bu yüzden değişkeni olmayan bir sürüm yazardım. Öte yandan, işlev çok basit, her şeyden önce çok önemli olduğunu sanmıyorum.

İşlev ne kadar uzun olursa ve değişkenler o kadar geniş kapsamdaysa sorun da o kadar fazla olur.

Örneğin, varsa

    a=getA();
    b=getB();
    m = ABFactory.mix(a,b);

Daha büyük bir fonksiyonun en üstünde, bir kod yerine üç değişken tanıtarak kodun geri kalanını anlamadaki zihinsel yükü arttırırsınız. Sen görmek için kod kalanını okumak zorunda olması aveya btekrar kullanılmaktadır. İhtiyaç duyduklarından daha uzun kapsamda olan yerel halk, genel okunabilirlik için kötüdür.

Değişken gerekli olduğu durumda Tabii (örneğin geçici bir sonucu depolamak için) veya değişken nerede yapar kodun okunabilirliği artırmak, o zaman tutulmalıdır.


2
Tabii ki, bir noktaya göre bir yöntem o kadar uzun sürüyor ki, yerel halkın sayısı kolayca korunamayacak kadar büyük, muhtemelen yine de ayrılmak istiyorsunuz.
Luaan

4
Gibi "yabancı" değişkenlerin yararları var result = getResult(...); return result;üzerine bir kesme noktası koymak returnve tam olarak ne olduğunu öğrenmek olabilir result.
Joker_vD

5
@Joker_vD: Adına yakışan bir hata ayıklayıcı, bunların hepsini yapmanıza izin verebilmelidir (bir işlev çağrısının sonuçlarını yerel bir değişkene kaydetmeden + işlevden çıkarken bir kesme noktası ayarlama).
Christian Hackl

2
@HristianHackl Çok az sayıda hata ayıklama özelliği, eklemeniz zaman alacak karmaşık saatler kurmadan (ve işlevlerin yan etkileri varsa her şeyi bozabilir) yapmanıza izin vermez. Haftanın herhangi bir günü hata ayıklama için değişkenler versiyonunu alacağım.
Gabe Sechan

1
@ChristianHackl ve hata ayıklayıcı korkunç bir şekilde tasarlandığından varsayılan olarak bunu yapmanıza izin vermese bile, bunun üzerine inşa etmek için, makinenizde yerel kodu değiştirerek sonucu depolayın ve ardından hata ayıklayıcısında kontrol edin. . Bir değişkenin içindeki bir değeri sadece bu amaç için saklamak için hala bir neden yoktur .
Büyük Ördek

11

Diğer cevaplara ek olarak, başka bir şeyi de işaret etmek istiyorum. Bir değişkenin kapsamını küçük tutmanın faydası, yalnızca değişkenin sözdizimsel olarak ne kadar kodun erişimine sahip olduğunu azaltmak değil, aynı zamanda bir değişkeni potansiyel olarak değiştirebilecek olası kontrol akışı yollarının sayısını da azaltmaktır (ya yeni bir değer atayarak veya arayarak) değişkende tutulan mevcut nesne üzerinde bir mutasyon yöntemi).

Sınıf kapsamındaki değişkenler (örnek veya statik), yerel kapsam kapsamındaki değişkenlerden önemli ölçüde daha fazla olası kontrol akışı yollarına sahiptir, çünkü bunlar herhangi bir sırayla, herhangi bir sayıda ve genellikle sınıf dışındaki kodla çağrılabilen yöntemlerle mutasyona uğrayabilirler .

İlk getResultyönteminize bir göz atalım :

public ABResult getResult() {
    getA();
    getB();
    return ABFactory.mix(this.a, this.b);
}

Şimdi, isimler getAve getBolabilir önermek onlar atamak olacağını this.ave this.bbiz bilemeyiz kesin sadece bakarak getResult. Bu nedenle, yönteme aktarılan this.ave this.bdeğerlerin mixyerine thisnesnenin durumundan gelmesi getResultmuhtemeldir, istemciler yöntemlerin nasıl ve ne zaman çağrıldığını kontrol ettiğinden tahmin etmek imkansızdır.

Lokal ave bdeğişkenlerle birlikte revize edilmiş kodda , her değişkenin kullanımına atanmasından tam bir (istisnasız) kontrol akışı olduğu açıktır, çünkü değişkenler kullanılmadan hemen önce bildirilir.

Bu nedenle, kontrol akışı gerekçesini basitleştirdiği için sınıf kapsamından yerel kapsama taşıma (değiştirilebilir) değişkenlerin (ayrıca bir döngünün dışından iç tarafa hareketli (değiştirilebilir) değişkenler) önemli bir faydası vardır.

Öte yandan, son örneğinizdeki gibi değişkenleri ortadan kaldırmanın daha az faydası vardır, çünkü kontrol akışı gerekçesini gerçekten etkilemez. Aynı zamanda, bir değişkeni içsel bir kapsama taşıdığınızda gerçekleşmeyen değerlere verilen isimleri de kaybedersiniz. Bu, göz önünde bulundurmanız gereken bir takastır, bu nedenle ortadan kaldırmak değişkenleri bazı durumlarda daha iyi olabilir, bazılarında daha kötü olabilir.

Değişken adlarını kaybetmek istemiyorsanız, ancak değişkenlerin kapsamını azaltmak istiyorsanız (daha büyük bir işlev içinde kullanılmaları durumunda), değişkenleri ve kullanımlarını bir blok ifadesinde kaydırmayı düşünebilirsiniz ( veya onları kendi işlevlerine taşıma ).


2

Bu biraz dile bağlı, ancak işlevsel programlamanın daha az belirgin yararlarından birinin programlayıcıyı ve kod okuyucuyu bunlara ihtiyaç duymamaya teşvik etmesi olduğunu söyleyebilirim. Düşünmek:

(reduce (fn [map string] (assoc map string (inc (map string 0))) 

Veya bazı LINQ:

var query2 = mydb.MyEntity.Select(x => x.SomeProp).AsEnumerable().Where(x => x == "Prop");

Veya Node.js:

 return visionFetchLabels(storageUri)
    .then(any(isCat))
    .then(notifySlack(secrets.slackWebhook, `A cat was posted: ${storageUri}`))
    .then(logSuccess, logError)
    .then(callback)

Sonuncusu, herhangi bir ara değişken olmadan, önceki bir fonksiyonun sonucuna bir fonksiyon çağırma zinciridir. Onları tanıtmak daha az netleşirdi.

Ancak, ilk örnek ile diğer iki arasındaki fark, zımni işlem sırasıdır . Bu, gerçekte hesaplandığı sırayla aynı olmayabilir, ancak okuyucunun bunun hakkında düşünmesi gereken sıradır. İkinci iki için bu sağdan sola. Lisp / Clojure örneği için sola doğru daha çok şey var. Diliniz için "varsayılan yönde" olmayan bir kod yazarken biraz dikkatli olmalısınız ve ikisini karıştıran "orta" ifadelerden kesinlikle kaçınılmalıdır.

F # 'nin boru operatörü |>kısmen kullanışlıdır, çünkü sağdan sola olması gereken soldan sağa şeyler yazmanıza izin verir.


2
Basit LINQ ifadelerinde veya lambda'larda alt çizgi olarak değişken adımı kullanmaya başladım. myCollection.Select(_ => _.SomeProp).Where(_ => _.Size > 4);
Graham

Bunun OP'nin sorusuna en ufak bir cevap verdiğinden emin değilim ...
AC

1
Neden indirimler? Bana iyi bir cevap gibi görünüyor, doğrudan soruya cevap vermese bile yine de yararlı bilgiler
reggaeguitar

1

Hayır derdim, çünkü "mümkün olan en küçük kapsamı", "mevcut kapsamlar veya eklenmesi makul olanlar arasında" olarak okumalısınız. Aksi taktirde {}, bir değişkenin kapsamının en son kullanım amacının ötesine geçmemesini sağlamak için suni kapsamlar (örneğin C benzeri dillerde bedava bloklar) oluşturmanız gerektiği anlamına gelir ve bu daha önce olmadığı sürece genellikle gizlenme / karışıklık olarak kaşlarını çatması gerekir. kapsamın bağımsız olması için iyi bir sebep.


2
Birçok dilde kapsam belirleme sorunlarından biri, son kullanımı diğer değişkenlerin başlangıç ​​değerlerinin hesaplanmasında bazı değişkenlere sahip olmanın son derece yaygın olmasıdır. Bir dil, kapsam belirleme amacıyla açıkça oluşturulmuşsa, kapsam dışı değişkenlerin başlatılmasında kullanılacak iç kapsam değişkenleri kullanılarak hesaplanan değerler için izin verilmişse, bu kapsamlar, birçok dilin ihtiyaç duyduğu kesinlikle yuvalanmış kapsamlardan daha yararlı olabilir.
supercat

1

İşlevleri düşünün ( yöntemler ). Bu, kodu ne küçük bir alt görevde, ne de en büyük tekil kod kodunda bölmez.

Mantıksal görevleri sınırlandırmakla birlikte sarf malzemelerine ayrılan bir sınırlamadır.

Aynısı değişkenler için de geçerlidir . Mantıksal veri yapılarını, anlaşılabilir parçalara işaret ederek. Ya da sadece parametreleri adlandırmak (belirtmek):

boolean automatic = true;
importFile(file, automatic);

Fakat elbette en tepede bir beyanda bulunmak ve iki yüz satır ileride ilk kullanım günümüzde kötü tarz olarak kabul edilmektedir. Bu, "değişkenlerin mümkün olan en küçük kapsamda yaşaması gerektiğini" açıkça söyleyeceğini açıklıyor. Çok yakın gibi "değişkenleri tekrar kullanmayın".


1

NO nedeni olarak biraz eksik olan şey hata ayıklama / okunabilirliktir. Kod bunun için optimize edilmeli ve net ve özlü isimler çok yardımcı olur, örneğin

if (frobnicate(x) && (get_age(x) > 2000 || calculate_duration(x) < 100 )

bu satır kısa, ancak okunması zaten zor. Birkaç parametre daha ekleyin ve eğer birden fazla satıra yayılırsa.

can_be_frobnicated = frobnicate(x)
is_short_lived_or_ancient = get_age(x) > 2000 || calculate_duration(x) < 100
if (can_be_frobnicated || is_short_lived_or_ancient )

Okumak ve anlam ifade etmek için bu yolu daha kolay buluyorum - bu yüzden ara değişkenlerle bir sorunum yok.

Başka bir örnek, R gibi diller olacaktır; burada son satır otomatik olarak geri dönüş değeridir:

some_func <- function(x) {
    compute(x)
}

bu tehlikelidir, dönüş hızlandırılmış mı yoksa gerekli mi? bu daha açık:

some_func <- function(x) {
   rv <- compute(x)
   return(rv)
}

Her zaman olduğu gibi, bu bir yargılama çağrısıdır - okumayı geliştirmezlerse aracı değişkenleri ortadan kaldırın, aksi halde saklayın veya tanıtın.

Başka bir nokta, hata ayıklama olabilir: Eğer orta dereceli sonuçlar ilgi çekiyorsa, yukarıdaki R örneğindeki gibi bir aracının tanıtılması en iyisi olabilir. Bunun ne sıklıkta çağrıldığını hayal etmek ve neyi kontrol ettiğinize dikkat etmek zordur - çok fazla hata ayıklama değişkeni kafa karıştırıcıdır - yine bir değerlendirme çağrısı.


0

Sadece başlığınıza atıfta bulunarak: Kesinlikle, eğer bir değişken gerekli değilse, silinmelidir.

Ancak “gereksiz”, değişken kullanmadan eşdeğer bir programın yazılabileceği anlamına gelmez, aksi takdirde her şeyi ikili olarak yazmamız gerektiği söylenir.

En yaygın gereksiz değişken türü, kullanılmayan bir değişkendir, değişkenin kapsamı ne kadar küçükse, gereksiz olduğunu belirlemek o kadar kolay olur. Bir ara değişkenin gereksiz olup olmadığını belirlemek zordur, çünkü bu ikili bir durum değildir, bağlamsaldır. Aslında, iki farklı yöntemdeki özdeş kaynak kodu, geçmiş kullanıcı deneyimini çevreleyen koddaki sorunlara bağlı olarak aynı kullanıcı tarafından farklı bir cevap üretebilir.

Örnek kodunuz tam olarak belirtildiyse, iki özel yöntemden kurtulmanızı öneririm, ancak fabrika aramalarının sonucunu yerel bir değişkene kaydettiğinizden veya bunları yalnızca karışımın argümanları olarak kullandığınızdan endişe duymazsınız. yöntem.

Kod okunabilirliği, doğru çalışmaktan başka bir şey yapmaz (doğru bir şekilde “olabildiğince hızlı” olan kabul edilebilir performans kriterlerini içerir).

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.