Geçici değişkenler ve satır uzunluğu gereksinimleri


10

Martin Fowler'in Refactoring'ini okuyorum . Genellikle mükemmeldir, ancak Fowler'in önerilerinden birinin biraz sorun yarattığı görülmektedir.

Fowler, bunun yerine geçici değişkenleri bir sorgu ile değiştirmenizi önerir:

double getPrice() {
    final int basePrice = _quantity * _itemPrice;
    final double discountFactor;
    if (basePrice > 1000) discountFactor = 0.95;
    else discountFactor = 0.98;
    return basePrice * discountFactor;
}

bir yardımcı yönteme geçersiniz:

double basePrice() {
    return _quantity * _itemPrice;
}

double getPrice() {
    final double discountFactor;
    if (basePrice() > 1000) discountFactor = 0.95;
    else discountFactor = 0.98;
    return basePrice() * discountFactor;
}

Genelde geçici değişkenler kullanmamın bir sebebinin bir çizginin çok uzun olması dışında katılıyorum. Örneğin:

$host = 'https://api.twilio.com';
$uri = "$host/2010-04-01/Accounts/$accountSid/Usage/Records/AllTime";
$response = Api::makeRequest($uri);

Bunu satır içine almayı denesem, satır 80 karakterden daha uzun olurdu.

Alternatif olarak, kendilerini okumak çok daha kolay olmayan kod zincirleriyle sonuçlanır:

$params = MustacheOptions::build(self::flattenParams($bagcheck->getParams()));

İkisini uzlaştırmak için bazı stratejiler nelerdir?


10
80 karakter monitörlerimin 1 / 3'ü kadardır. 80 karakter çizgisine yapışmanın sizin için hala değerli olduğuna emin misiniz?
jk.


Sizin $hostve $uriörneğiniz bir tür uyuşuk olsa da - ana bilgisayar bir ayardan veya başka bir girdiden okunmadıkça, onları sarın veya kenardan çıksa bile aynı satırda olmalarını tercih ederim.
İzkata

5
Bu kadar dogmatik olmaya gerek yok. Kitap, her yerde, her seferinde uygulamak zorunda olduğunuz bir kurallar dizisi değil, yardım ederken kullanılabilecek tekniklerin bir listesidir. Mesele, kodunuzu daha sürdürülebilir ve okunması daha kolay hale getirmektir. Bir refactor bunu yapmazsa, kullanmazsınız.
Sean McSomething

80 karakterlik bir sınırın biraz fazla olduğunu düşünsem de, benzer bir sınırlama (100?) Makul. Örneğin, portre odaklı monitörlerde programlamayı seviyorum, bu yüzden ekstra uzun çizgiler can sıkıcı olabilir (en azından ortaksa).
Thomas Eding

Yanıtlar:


16

Nasıl Yapılır
1. Satır uzunluğu kısıtlamaları vardır, böylece daha fazla kodu görebilir ve anlayabilirsiniz . Hala geçerlidir.
2. Kör sözleşmeye ilişkin yargıyı vurgulayın .
3. Performansı optimize etmedikçe geçici değişkenlerden kaçının .
4. Çok satırlı ifadelerde hizalama için derin girinti kullanmaktan kaçının.
5. Uzun ifadeleri fikir sınırları boyunca birden çok satıra ayırın :

// prefer this
var distance = Math.Sqrt(
    Math.Pow(point2.GetX() - point1.GetX(), 2) + // x's
    Math.Pow(point2.GetY() - point1.GetY(), 2)   // y's
);

// over this
var distance = Math.Sqrt(Math.Pow(point2.GetX() -
    point1.GetX(), 2) + Math.Pow(point2.GetY() -
    point1.GetY(), 2)); // not even sure if I typed that correctly.

Akıl Yürütme
Geçici değişkenlerle ilgili (hata ayıklama) sorunların temel kaynağı, değişken olma eğiliminde olmalarıdır. Yani, kodu yazarken bir değer olduklarını varsayacağım, ancak işlev karmaşıksa, başka bir kod parçası durumlarını yarıya kadar değiştirir. (Ya da değişkenin durumunun aynı kaldığı ancak sorgunun sonucu değiştiği converse).

Performans için optimizasyon yapmadığınız sürece sorgulara sadık kalmayı düşünün . Bu, bu değeri hesaplamak için kullandığınız mantığı tek bir yerde tutar.

Verdiğiniz örnekler (Java ve ... PHP?) Çok satırlı ifadelere izin verir. Çizgiler uzarsa, onları ayırın. Jquery kaynağı bunu uç noktalara götürüyor. (İlk ifade satır 69 çalışır!) Ben kesinlikle katılıyorum değil, ama temp vars kullanmaktan daha kodunuzu okunabilir yapmak için başka yollar vardır.

Bazı Örnekler
1. PEPhon için PEP 8 Stil Kılavuzu (en güzel örnek değil)
2. Armut Stil Kılavuzunda Paul M Jones (yol argümanının ortasında)
3. Oracle çizgi uzunluğu + sarma kuralları (80 karaktere kadar tutmak için yararlı etiketler)
4. MDN Java Uygulamaları (programcının konvansiyon hakkındaki kararını vurgular)


1
Sorunun diğer kısmı, geçici bir değişkenin değerinden genellikle daha uzun süre dayanmasıdır. Küçük kapsam bloklarında sorun değil, büyük bloklarda evet, büyük bir sorun.
Ross Patterson

8
Geçici olarak değiştirilmesinden endişe ediyorsanız, bir const koyun.
Thomas Eding

3

Bence geçici değişkenler yerine yardımcı yöntemleri kullanmak için en iyi argüman insanın okunabilirliğidir. Eğer bir insan olarak, yardımcı yöntem zincirini okumak için geçici varialbe göre daha fazla sorun yaşıyorsanız, onları çıkarmanız için hiçbir neden göremiyorum.

(Yanılıyorsam lütfen beni düzeltin)


3

80 karakter yönergesine kesinlikle uymanız gerektiğini veya yerel geçici değişkenin çıkarılması gerektiğini düşünmüyorum. Fakat aynı düşünceyi ifade etmenin daha iyi yolları için uzun çizgiler ve yerel temperler araştırılmalıdır. Temel olarak, belirli bir fonksiyonun veya hattın çok karmaşık olduğunu ve onu parçalamamız gerektiğini gösterirler. Ancak dikkatli olmalıyız, çünkü bir görevi kötü bir şekilde parçalara ayırmak durumu daha karmaşık hale getirir. Bu yüzden işleri dayanıklı ve basit bileşenlere ayıracağım.

Gönderdiğiniz örneklere bakayım.

$host = 'https://api.twilio.com';
$uri = "$host/2010-04-01/Accounts/$accountSid/Usage/Records/AllTime";
$response = Api::makeRequest($uri);

Benim gözlemim, tüm twilio api çağrılarının "https://api.twilio.com/2010-04-1/" ile başlayacağı ve bu nedenle sahip olunması gereken çok açık bir yeniden kullanılabilir fonksiyonun olduğudur:

$uri = twilioURL("Accounts/$accountSid/Usage/Records/AllTime")

Aslında, bir URL oluşturmak için tek nedeni istekte bulunmak olduğunu, bu yüzden yapardım:

$response = TwilioApi::makeRequest("Accounts/$accountSid/Usage/Records/AllTime")

Aslında, URL'lerin çoğu aslında "Hesaplar / $ accountSid" ile başlar, bu yüzden muhtemelen bunu da çıkartacağım:

$response = TwilioApi::makeAccountRequest($accountSid, "Usage/Records/AllTime")

Ve twilio api'yi hesap numarasını tutan bir nesne yaparsak, şöyle bir şey yapabiliriz:

$response = $twilio->makeAccountRequest("Usage/Records/AllTime")

$ Twilio nesnesi kullanmak, birim testini kolaylaştırır. Nesneye, twilio'ya geri dönmeyen, daha hızlı olacak ve twilio'ya garip şeyler yapmayacak farklı bir $ twilio nesnesi verebilirim.

Diğerine bakalım

$params = MustacheOptions::build(self::flattenParams($bagcheck->getParams()));

Burada ben de düşünürdüm:

$params = MustacheOptions::buildFromParams($bagcheck->getParams());

veya

$params = MustacheOptions::build($bagcheck->getFlatParams());

veya

$params = MustacheOptions::build(flatParams($backCheck));

Hangisine bağlı olarak daha yeniden kullanılabilir deyim.


1

Aslında, genel davada seçkin Bay Fowler ile aynı fikirde değilim.

Önceden satırlanmış koddan bir yöntemin çıkarılmasının avantajı, kodun yeniden kullanılmasıdır; yöntemdeki kod artık ilk kullanımından kopuyor ve artık kopyalanıp yapıştırılmadan koddaki başka yerlerde de kullanılabiliyor (kopyalanan kodun genel mantığı değişmek zorundaysa birden fazla yerde değişiklik yapılmasını gerektirecek) .

Ancak, eşit, genellikle daha büyük kavramsal değer "değerin yeniden kullanımı" dır. Bay Fowler, bu değişken yöntemleri geçici değişkenler "sorgularının" yerine çağırıyor. Peki, daha verimli olan; bir veritabanını belirli bir değere ihtiyacınız olan birden çok kez sorgulama veya bir kez sorgulama ve sonucu saklama (değerin değişmesini beklemeyeceğiniz kadar statik olduğu varsayılarak)?

Örneğinizdeki nispeten önemsiz olanın ötesinde hemen hemen her hesaplama için, çoğu dilde, bir hesaplamanın sonucunu hesaplamaya devam etmekten daha ucuzdur. Bu nedenle, talep üzerine yeniden hesaplamak için genel öneri tutarsızdır; daha fazla geliştirici zamanına ve daha fazla CPU zamanına mal olur ve modern sistemlerin çoğunda bu üçünün en ucuz kaynağı olan önemsiz bir bellek tasarrufu sağlar.

Şimdi, yardımcı yöntem, diğer kodla birlikte, "tembel" hale getirilebilir. İlk çalıştırıldığında bir değişken başlatır. Diğer tüm çağrılar, yönteme açıkça yeniden hesaplaması söylenene kadar bu değişkeni döndürür. Bu, yöntemin bir parametresi veya bu yöntemin hesaplanmasının bağlı olduğu herhangi bir değeri değiştiren başka bir kod tarafından ayarlanan bir bayrak olabilir:

double? _basePrice; //not sure if Java has C#'s "nullable" concept
double basePrice(bool forceCalc)
{
   if(forceCalc || !_basePrice.HasValue)
      return _basePrice = _quantity * _itemPrice;
   return _basePrice.Value;
}

Şimdi, bu önemsiz hesaplama için kaydedilenden daha fazla iş yapıldı ve bu yüzden genellikle temp değişkenine bağlı kalmanızı tavsiye ederim; ancak, genellikle birden çok kez çalıştırmaktan kaçınmak istediğiniz ve kodun birden çok yerinde ihtiyaç duyduğunuz daha karmaşık hesaplamalar için, bunu yapmanın yolu budur.


1

Yardımcı yöntemlerin bir yeri vardır, ancak verilerin tutarlılığını ve değişkenlerin kapsamındaki gereksiz artışı sağlama konusunda dikkatli olmalısınız.

Örneğin, kendi örneğiniz:

double getPrice() {
    final double discountFactor;
    if (basePrice() > 1000) discountFactor = 0.95;      <--- first call
    else discountFactor = 0.98;
    return basePrice() * discountFactor;                <--- second call
}

Açıkça her ikisi de _quantityve _itemPriceküresel değişkenlerdir (veya en azından sınıf düzeyinde) ve bu nedenle bunların dışında değişiklik yapma potansiyeli vardır.getPrice()

Bu nedenle, ilk çağrı için basePrice()ikinci çağrıdan farklı bir değer döndürme potansiyeli vardır !

Bu nedenle, yardımcı fonksiyonların karmaşık matematiği izole etmek için yararlı olabileceğini önerebilirim, ancak yerel değişkenlerin yerine, dikkatli olmanız gerekir.


Ayrıca indirgeme reklam absurdum kaçınmak gerekir - hesaplanması discountFactorbir yöntem kapalı tutulmalıdır? Böylece örneğin:

double getPrice()
{
    final double basePrice      = calculateBasePrice();
    final double discountFactor = calculateDiscount( basePrice );

    return basePrice * discountFactor;
}

Belirli bir düzeyin ötesine bölümleme aslında kodu daha az okunabilir hale getirir.


Kodu daha az okunabilir yapmak için +1. Aşırı bölümleme, kaynak kodunun çözmeye çalıştığı iş sorununu gizleyebilir. GetPrice () içinde bir kuponun uygulandığı özel durumlar olabilir, ancak bu bir işlev çağrıları zincirinin derinliklerinde gizlenmişse, iş kuralı da gizlenir.
Reactgular

0

Adlandırılmış parametrelerle (ObjectiveC, Python, Ruby, vb.) Bir dilde çalışırsanız, geçici değişkenler daha az kullanışlıdır.

Ancak, basePrice örneğinizde, sorgunun yürütülmesi biraz zaman alabilir ve sonucu ileride kullanmak üzere geçici bir değişkende saklamak isteyebilirsiniz.

Yine de sizin gibi, netlik ve satır uzunluğu konuları için geçici değişkenler kullanıyorum.

Ben de programcılar PHP aşağıdaki yapmak gördüm. Hata ayıklamak için ilginç ve harika, ama biraz garip.

$rs = DB::query( $query = "SELECT * FROM table" );
if (DEBUG) echo $query;
// do something with $rs

0

Bu önerinin ardındaki gerekçe, başvurunuzda başka bir yerde aynı ön hesaplamayı kullanabilmenizdir. Yeniden düzenleme kalıpları kataloğundaki Sıcaklığı Sorgu ile Değiştirme bölümüne bakın :

Yeni yöntem daha sonra diğer yöntemlerde kullanılabilir

    double basePrice = _quantity * _itemPrice;
    if (basePrice > 1000)
        return basePrice * 0.95;
    else
        return basePrice * 0.98;

           http://i.stack.imgur.com/mKbQM.gif

    if (basePrice() > 1000)
        return basePrice() * 0.95;
    else
        return basePrice() * 0.98;
...
double basePrice() {
    return _quantity * _itemPrice;
}

Bu nedenle, ana makineniz ve URI örneğinizde, bu öneriyi yalnızca aynı URI'yi veya ana bilgisayar tanımını yeniden kullanmayı planlıyorsam uygularım.

Bu durumda, ad alanı nedeniyle bir global uri () veya host () yöntemi tanımlamayacağım, ancak twilio_host () veya archive_request_uri () gibi daha fazla bilgi içeren bir ad tanımlayamam.

Ardından, satır uzunluğu sorunu için birkaç seçenek görüyorum:

  • Gibi yerel bir değişken oluşturun uri = archive_request_uri().

Gerekçe: Geçerli yöntemde, URI'nin belirtilen seçenek olmasını istiyorsunuz. URI tanımı hala çarpanlarına ayrılmıştır.

  • Gibi yerel bir yöntem tanımlayın uri() { return archive_request_uri() }

Fowler'in önerisini sık sık kullanıyorsanız, uri () yönteminin aynı kalıp olduğunu bilirsiniz.

Dil seçimi nedeniyle yerel yönteme bir 'benlik' ile erişmeniz gerekiyorsa, daha fazla ifade için ilk çözümü öneririm (Python'da uri işlevini geçerli yöntem içinde tanımları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.