Temp'i Query ile Değiştirme: Bir Yeniden Düzenleme Tekniği

Temp'i Query ile Değiştirme: Daha Temiz ve Daha Test Edilebilir Kod için Bir Yeniden Düzenleme Tekniği

Geçici değişkenler, yazılım kodundaki gereksiz karmaşıklığın en yaygın kaynaklarından biridir. Uzun metotlarda birikirler, hesaplanan değerlere belirsiz isimler verirler ve içerdikleri mantığı ayıklamayı, test etmeyi veya yeniden kullanmayı zorlaştırırlar. Martin Fowler'ın "Refactoring: Improving the Design of Existing Code" adlı kitabında katalogladığı "Geçici Değişkeni Sorgu ile Değiştirme" yeniden düzenleme yöntemi , bu sorunu doğrudan ele alır: hesaplanan bir değeri yerel bir değişkende saklamak yerine, hesaplamayı adlandırılmış bir metoda, yani bir sorguya çıkarır ve değerin gerektiği her yerde çağırırsınız.

Sonuç olarak, kod amacını gizlemek yerine açıkça iletiyor. Hesaplama artık uzun bir metodun başındaki değişken atamasına gömülmüyor; bir adı, konumu ve bağımsız olarak test edilebilme özelliği var. Bu makale, tekniğin tamamını, geçici değişkenlerin ne olduğunu, ne zaman sorun haline geldiklerini, Java, Python ve TypeScript'te adım adım yeniden düzenlemenin nasıl yapılacağını, ne zaman uygulanacağını ve ne zaman uygulanmayacağını ve yeniden düzenleme kataloğundaki ilgili tekniklerle nasıl bağlantılı olduğunu ele almaktadır.

Kodunuzu Güvenle Yeniden Düzenleyin

SMART TS XL Geçici değişkenlerin, tekrarlanan hesaplamaların ve çıkarılan sorgu yöntemlerinin kullanıldığı izler.

Daha fazla bilgi edin

Programlamada Geçici Değişken (Temp) Nedir?

Geçici değişken, genellikle " temp" olarak adlandırılır ve bir fonksiyon veya metot içinde, aynı kapsam içinde kullanılmak üzere ara bir sonucu saklayan yerel bir değişkendir. Bir kez hesaplanır, adlandırılmış bir değişkende tutulur ve daha sonra aynı fonksiyonda referans alınır. Değişken yalnızca fonksiyon çağrısının ömrü boyunca mevcuttur; dışında erişilemez ve nesnenin durumunda saklanmaz.

piton

# Python: base_price is a temp variable
def calculate_total(quantity, item_price):
    base_price = quantity * item_price   # temp: computed once, used below
    if base_price > 1000:
        return base_price * 0.95
    return base_price * 0.98

Java

// Java: basePrice is a temp variable
double basePrice = quantity * itemPrice;   // temp
if (basePrice > 1000) {
    return basePrice * 0.95;
}
return basePrice * 0.98;

daktilo ile yazılmış yazı

// TypeScript: basePrice is a temp variable
const basePrice = quantity * itemPrice;    // temp
if (basePrice > 1000) return basePrice * 0.95;
return basePrice * 0.98;

Geçici değerler özünde kötü değildir. Meşru kullanım alanları vardır: tekrarlanması israf olacak pahalı bir işlemin sonucunu yakalamak, karmaşık çok adımlı bir hesaplamayı okunabilir aşamalara bölmek veya döngü yinelemeleri boyunca biriken değerleri tutmak gibi. Sorun, geçici değerlerin, adlandırılmış yöntemler olarak daha açık olabilecek basit türetilmiş değerler için refleksif olarak kullanılması veya uzun bir yöntem boyunca birikerek okuyucuları aynı anda aktif olan birden fazla ara değeri izlemeye zorlaması durumunda ortaya çıkar.

Yazılım Mühendisliğinde Yeniden Yapılandırma (Refactoring) Nedir?

Kodun yeniden yapılandırılması (refactoring), gözlemlenebilir davranışını değiştirmeden mevcut kodu yeniden yapılandırma işlemidir. Amaç, kodun içsel kalitesini iyileştirmektir: okunabilirliği, test edilebilirliği, sürdürülebilirliği ve modülerliği. Yeniden yapılandırma, özellik eklemez ve hataları düzeltmez; kodun yapısını değiştirirken işlevini korur.

Temp'i Query ile Değiştirme, Martin Fowler tarafından açıklanan onlarca teknikten oluşan bir katalogdaki yeniden düzenleme yöntemlerinden biridir. Çok uzun veya çok karmaşık hale gelen metotlarla ilgilenen teknikler ailesine aittir:

Yeniden Yapılandırma TekniğiBu ne yapar
Temp'i Sorgu ile DeğiştirGeçici bir değişkenin hesaplamasını adlandırılmış bir metoda çıkarır.
Ekstrakt YöntemiBir kod bloğunu yeni bir adlandırılmış metoda çıkarır.
Satır İçi SıcaklıkBasit bir sıcaklığı doğrudan ifadesiyle değiştirir.
Geçici Değişkeni BölFarklı amaçlar için yeniden kullanılan geçici bir değeri ayrı değişkenlere ayırır.
Döngüyü Boru Hattı ile DeğiştirinZorunlu bir döngüyü işlevsel bir işlem hattıyla (map, filter, reduce) değiştirir.
Açıklayıcı Değişkeni TanıtınKarmaşık bir ifadeyi açıklığa kavuşturmak için adlandırılmış bir geçici değer sunar.

Bu teknikler tek başına kullanılmaz. Fowler, Temp'i Sorgu ile Değiştirme işlemini Metot Çıkarma işleminden önce hayati bir adım olarak tanımlar: eğer bir metotta geçici değişkenler varsa, bu geçici değişkenler çıkarılan bölümden önce ve sonra kullanılabileceğinden, bu metodun bir bölümünü yeni bir metoda çıkarmak zorlaşır. Geçici değişkenleri önce sorgulara dönüştürerek ortadan kaldırmak, çıkarma işleminin yolunu açar.

"Geçici Koşulları Sorguyla Değiştirme" ne anlama geliyor?

"Temp'i Sorgu ile Değiştirme", yerel geçici bir değişkeni bir metot çağrısına dönüştüren bir yeniden düzenleme tekniğidir. Bir değer hesaplayıp yerel bir değişkene atamak yerine, hesaplamayı özel bir metoda, yani sorguya çıkarırsınız ve bu metot çağrıldığında hesaplanan değeri döndürür. Temp'in kullanıldığı her yerde, referansı sorgu metoduna yapılan bir çağrı ile değiştirirsiniz.

Fowler'ın Refactoring kitabından alınan temel örnek :

Önce:

Java

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

Sonra:

Java

if (basePrice() > 1000)
    return basePrice() * 0.95;
else
    return basePrice() * 0.98;

private double basePrice() {
    return _quantity * _itemPrice;
}

Sorgu yöntemi basePrice() Artık adlandırılmış, kendi kendine yeten bir hesaplama haline geldi. Sınıftaki herhangi bir başka metottan çağrılabilir, bağımsız olarak test edilebilir, alt sınıflarda geçersiz kılınabilir ve çağıran metodu önce okumaya gerek kalmadan anlaşılabilir.

Geçici Değişkenlerle İlgili Sorun

Mantığı bir yöntem üzerinden parçalara ayırıyorlar.

Geçici bir değişken, bir hesaplamayı iki ayrı parçaya böler: atama (değerin hesaplandığı yer) ve kullanım (değerin okunduğu yer). Kısa bir metotta bu ayrım zararsızdır. Otuz veya elli satıra ulaşmış bir metotta, atama ve kullanım, birçok satırlık başka mantıkla ayrılmış olabilir. Okuyucu, atamayı bulmak için yukarı kaydırmalı, anlamını çalışma belleğinde tutmalı ve kullanıma geri dönmelidir. Her ek geçici değişken, bu bilişsel yükü artırır.

Çıkarma Yöntemini Engelliyorlar

Geçici değişkenlerle ilgili en önemli pratik sorun, diğer yeniden düzenlemeleri engellemeleridir. Karmaşık bir koşullu dallanma içeren ve ayrı bir metoda çıkarılması faydalı olacak bir metodu düşünün. Eğer dallanma, metodun daha önceki bir aşamasında atanmış bir geçici değişken kullanıyorsa, çıkarma işlemi ya geçici değişkeni parametre olarak geçirmeyi, geçici değişkeni bir örnek değişkeni yapmayı ya da çıkarılan metodun içinde değerini tekrar hesaplamayı gerektirir. Bu seçeneklerin hiçbiri temiz bir çözüm değildir. Geçici değişkeni bir sorgu ile değiştirerek önce ortadan kaldırmak, bu engeli tamamen ortadan kaldırır.

Yeniden kullanıma ve değişime davet ediyorlar.

Geçici değişkenler bazen aynı metot içinde farklı amaçlar için yeniden kullanılır; Fowler bu uygulamayı "geçici değişken karmaşası" olarak adlandırır. Örneğin, bir değişkenin adı... temp or result Birden fazla kez yeniden atanan geçici değerler, anlamsal bilgi sağlamaz ve okuyucuları herhangi bir anda neyi temsil ettiği konusunda aktif olarak yanıltır. Tek amaçlı geçici değerler bile birikerek, bir metodun kapsamını okuyucuların aynı anda takip etmesi gereken ara değerlerle doldurabilir.

Adım Adım: Geçici Değeri Sorgu ile Değiştirme İşlemi Nasıl Uygulanır?

Bu dönüşüm, herhangi bir dilde güvenle uygulanabilen dört adımdan oluşmaktadır:

Adım 1: Geçici değişkenin yalnızca bir kez atandığından ve asla değiştirilmediğinden emin olun. Geçici değişken daha sonra metot içinde yeniden atanırsa, önce Geçici Değişkeni Böl seçeneğini kullanarak onu bölün.

Adım 2: Atamanın sağ tarafını özel bir metoda çıkarın. Metoda, nasıl hesapladığını değil, neyi hesapladığını açıklayan bir isim verin. basePrice() daha iyi calculateQuantityTimesPrice().

3. Adım: temp'e yapılan her referansı yeni metoda yapılan bir çağrı ile değiştirin. Çoğu IDE bunu otomatik olarak yapabilir: temp'e sağ tıklayın → Yeniden Düzenle → Değişkeni Satır İçi Yap, ardından satır içi yapılmış ifadede Metodu Çıkar'ı seçin.

4. Adım: temp değişken bildirimini silin. Çıkarma işlemi tamamlandıysa, temp'in artık hiçbir referansı kalmamış olmalı ve kaldırılabilir.

Java: Tam Çalışan Örnek

Java

// Before: Order class with temporary variables
public class Order {
    private int quantity;
    private double itemPrice;

    public double getPrice() {
        double basePrice    = quantity * itemPrice;        // temp 1
        double discountFactor;                             // temp 2
        if (basePrice > 1000)
            discountFactor = 0.95;
        else
            discountFactor = 0.98;
        return basePrice * discountFactor;
    }
}

Java

// After: temps extracted to query methods
public class Order {
    private int quantity;
    private double itemPrice;

    public double getPrice() {
        return basePrice() * discountFactor();
    }

    private double basePrice() {
        return quantity * itemPrice;
    }

    private double discountFactor() {
        return basePrice() > 1000 ? 0.95 : 0.98;
    }
}

Gibi bir yöntem getPrice() Artık hesaplamayı açıkça ileten tek bir ifade olarak okunuyor. Çıkarılan her sorgu bağımsız olarak okunabilir, test edilebilir ve genişletilebilir. Şunu unutmayın ki... discountFactor() aramalar basePrice()Bu doğru çünkü basePrice() Bu, yan etkileri olmayan saf bir hesaplama işlemidir, bu nedenle iki kez çağrılması herhangi bir risk oluşturmaz.

Python: Temp'i Property ile Değiştirme

Python'da sorgu metodunun doğal karşılığı şudur: @propertyBu yöntem, parantez kullanılmadan çağrılabilmesini sağlar ve bir öznitelik erişimiyle tamamen aynı şekilde okunur:

piton

# Before: temporary variables in a method
class Order:
    def __init__(self, quantity, item_price):
        self.quantity   = quantity
        self.item_price = item_price

    def get_price(self):
        base_price     = self.quantity * self.item_price  # temp
        discount       = 0.95 if base_price > 1000 else 0.98  # temp
        return base_price * discount

piton

# After: temps replaced with properties (query methods in Python)
class Order:
    def __init__(self, quantity, item_price):
        self.quantity   = quantity
        self.item_price = item_price

    def get_price(self):
        return self.base_price * self.discount_factor

    @property
    def base_price(self):
        return self.quantity * self.item_price

    @property
    def discount_factor(self):
        return 0.95 if self.base_price > 1000 else 0.98

kullanma @property anlamına geliyor self.base_price Bu, bir örnek değişkeniyle tamamen aynı şekilde okunur ve çağıran kodun çalışmasını kolaylaştırır. self.base_price * self.discount_factor Tamamen doğal. Her özellik bağımsız olarak test edilebilir:

piton

def test_base_price():
    order = Order(10, 150)
    assert order.base_price == 1500

def test_discount_factor_high_value():
    order = Order(10, 150)   # base_price = 1500 > 1000
    assert order.discount_factor == 0.95

def test_get_price():
    order = Order(10, 150)
    assert order.get_price() == 1500 * 0.95

Bu seviyede test edilebilirlik, geçici değerlere dayalı sürümde mümkün değildir: iç hesaplamalar base_price hem de discount_factor Bu yöntemlere dışarıdan erişilemez.

TypeScript: Sorgu Metotları ve Getters

TypeScript, Java ve Python'da bulunan kalıplara uygun olarak hem metot tabanlı sorguları hem de özellik alıcılarını destekler:

daktilo ile yazılmış yazı

// Before: temporary variables
class Order {
    constructor(private quantity: number, private itemPrice: number) {}

    getPrice(): number {
        const basePrice = this.quantity * this.itemPrice;  // temp
        const discount  = basePrice > 1000 ? 0.95 : 0.98; // temp
        return basePrice * discount;
    }
}

daktilo ile yazılmış yazı

// After: TypeScript getters replace temps
class Order {
    constructor(private quantity: number, private itemPrice: number) {}

    getPrice(): number {
        return this.basePrice * this.discountFactor;
    }

    private get basePrice(): number {
        return this.quantity * this.itemPrice;
    }

    private get discountFactor(): number {
        return this.basePrice > 1000 ? 0.95 : 0.98;
    }
}

Sorgu Yöntemlerine İyi İsim Verme

Sorgu yönteminin adı en önemli işi yapıyor. Kötü adlandırılmış bir veri çıkarma işlemi, yerini aldığı geçici veriden daha kötüdür çünkü anlaşılması güç bir dolaylılık yaratır: çağıranların ne yaptığını anlamak için yöntem tanımına gitmeleri gerekir, bu da amacın boşa çıkmasına neden olur.

İyi sorgu yöntemi adları şu prensiplere uyar:

Hesaplanma şeklini değil, neyi temsil ettiğini belirtin. basePrice() İş konseptini iletiyor. getQuantityTimesItemPrice() Hesaplamayı tanımlar, kavramı değil. Hesaplama değiştiğinde bu ayrım önem kazanır, kavram adı da değişir. basePrice() Formül değişse bile istikrarlı kalır.

Değerleri belirtmek için isim tamlamaları kullanın. Sorgulama yöntemleri değer döndürür; komut değillerdir. discountFactor(), totalAmount(), isEligible() Döndürülen veriye isim verme kuralına uyun. calculateDiscount(), processAmount() Yalnızca hesaplama yapıp sonuç döndüren yöntemler için kafa karıştırıcı olan, emir kipiyle yazılmış komutlara uyulması gerekir.

Mantıksal sorgular soru şeklinde okunmalıdır. isHighValue(), hasDiscount(), meetsThreshold() Dönüş değerinin bir boolean olduğunu ve arayanın evet/hayır sorusu sorduğunu iletin. bool 変数名 (Mantıksal değişken adlandırması, Arama Konsolu verilerindeki bir sorgu) tam olarak bu endişeyi yansıtıyor: mantıksal değişkenler ve yöntemler, kullanım noktasında anlamlarını netleştiren adlara ihtiyaç duyar.

İlgili yöntemler arasında isimleri sabit tutun. If basePrice() tarafından kullanılır discountFactor()İsimlendirmedeki tutarlılık okuyuculara şunu anlatıyor: discountFactor göre değişir basePriceTutarsız isimlendirme bu örtük dokümantasyonu bozar.

Geçici değeri sorgu ile değiştirme işlemi ne zaman uygulanmalı?

Bu yeniden düzenlemeyi şu durumlarda uygulayın:

  • Geçici çalışan yalnızca bir kez atanır ve asla yeniden atanmaz.
  • Hesaplama tamamen ifadeseldir: alanlardan veya parametrelerden okuma yapar ancak harici durumu değiştirmez, ağ hizmetlerini çağırmaz veya zamana ya da rastgeleliğe bağlı değildir.
  • Hesaplama o kadar karmaşık ki, ona bir isim vermek okunabilirliği artırır; ya da o kadar basit ki, geçici değer sadece gereksiz bir karmaşa yaratır.
  • Temp kullanan bir bloğa Çıkarma Yöntemi uygulamak üzeresiniz.

En yaygın ideal senaryo, türetilmiş bir değerdir: fiyat, toplam, indirim, biçimlendirilmiş bir dize, koşullu bir sınıflandırma. Bunlar, nesnenin alanlarından tamamen türetilen, yan etkileri olmayan ve bir metodun içindeki ara hesaplamalar yerine nesnenin özellikleri olarak doğal bir şekilde yer alan değerlerdir.

Temp yerine Query kullanılmaması gereken durumlar

Performansa duyarlı işlemler. Hesaplama maliyetliyse, bir veritabanı sorgusu, bir ağ çağrısı, bir O(n²) döngüsü veya sorgu yöntemini iki kez çağırmak maliyeti iki katına çıkarır. Geçici değişken tam olarak bunu önlemek için vardır. Bu durumlarda, ya geçici değişkeni yerinde bırakın ya da sorgu yöntemini memoize edin (ilk çağrıdan sonra sonucu önbelleğe alın):

piton

# Memoized property: computed once, cached
from functools import cached_property

class Order:
    @cached_property
    def expensive_validation(self):
        return self.external_service.validate(self.data)  # called once, cached

Yan etkili işlemler. Eğer geçici değişken yalnızca bir kez çalıştırılması gereken bir işlemin sonucunu tutuyorsa (benzersiz bir kimlik oluşturma, günlük kaydı tutma, dosyaya yazma), bunu bir sorguya dönüştürmek, işlemi her çağrıda çalıştıracaktır. Bu, programın yalnızca yapısını değil, davranışını da değiştirir. Bu yeniden düzenlemeyi yan etkili geçici değişkenlere uygulamayın.

Döngü yinelemeleri boyunca biriken sıcaklıklar. Bir akümülatördeki sıcaklık for döngü, total += item.priceBu, "Geçici Değeri Sorgu ile Değiştir" seçeneği için uygun bir aday değildir. Türetilmiş bir değer değil; yinelemeler boyunca biriken bir durumdur. Eğer sorun döngüdeyse, bunun yerine "Döngüyü İşlem Hattı ile Değiştir" seçeneğini değerlendirin.

İlgili Yeniden Yapılandırma Teknikleri

"Temp'i Sorgu ile Değiştir" tekniği, metotlardaki gereksiz karmaşıklığı ortadan kaldıran bir teknik ailesine aittir. Bu teknik ailesini anlamak, geliştiricilerin eldeki sorun için doğru tekniği seçmelerine yardımcı olur:

Extract Method en yaygın kullanılan yardımcı araçtır. Replace Temp with Query, aksi takdirde çıkarılan kısım ile metodun geri kalanı arasında garip parametre geçişleri gerektirecek değişkenleri temizleyerek Extract Method'u mümkün kılar.

Inline Temp, bir temp nesnesi eklemenin tersidir: kodun içinde temp nesnesinin yerine doğrudan ifadesini koyar. Temp nesnesinin herhangi bir açıklık sağlamadığı ve ifadesinin zaten okunabilir olduğu durumlarda Inline Temp kullanın.

Geçici Değişkeni Bölme özelliği, aynı metotta tek bir geçici değişkenin birden fazla amaç için yeniden kullanılması durumunda uygulanır. Bu özelliği, her bir amacı yansıtan isimlere sahip ayrı değişkenlere bölün ve ardından ortaya çıkan tek kullanımlık geçici değişkenlerden herhangi birine Geçici Değişkeni Sorgu ile Değiştir özelliğini uygulayın.

Açıklayıcı Değişken Tanıtma ise tam tersi yöndedir: karmaşık bir ifadenin okunması zorsa, açıklayıcı bir isimle geçici bir değişken tanıtmak anlaşılırlığı artırabilir. Bu teknik ve Geçici Değişkeni Sorgu ile Değiştirme birbirine zıttır ve geliştirici, hangi yöntemin söz konusu kodda iyileşme sağladığına karar vermelidir.

Döngüyü Boru Hattı ile Değiştirin Bu, geçici bir biriktiriciye sahip bir döngünün zincirleme bir işlem hattı operasyonuyla değiştirilebileceği yaygın bir kalıbı ele almaktadır (map, filter, reduceBu daha açıklayıcı ve okunması daha kolaydır.

Ne kadar SMART TS XL Büyük Ölçekte Yeniden Yapılandırmayı Destekler

Temp'i Query ile değiştirmek yerel bir yeniden düzenlemedir: bir metottaki bir değişkeni dönüştürür. Önemli büyüklükteki bir kod tabanında, daha yararlı soru "bu yeniden düzenlemeyi nasıl uygulayacağım?" değil, "tüm kod tabanında nerede uygulamalıyım ve uyguladığımda neler etkilenecek?" olmalıdır.

SMART TS XL Bu, soruyu sistematik bir şekilde yanıtlamayı sağlayan, kod tabanları arası yapısal analiz sunar. Aynı hesaplamanın birden fazla yerde geçici bir değişken olarak gerçekleştirildiği yerleri belirler; bu, Replace Temp with Query'nin tek bir adlandırılmış sorgu yönteminde birleştirmeyi amaçladığı kalıptır. Yeniden düzenlenmiş bir sorgu yönteminin çıkarıldıktan sonra nasıl kullanıldığını izler ve yeniden düzenlemenin kapsamını yapılmadan önce görünür hale getirir. Ve diller arası çalışır: COBOL programlarının, Java servislerinin ve Python işlem hatlarının aynı veriler üzerinde çalıştığı kurumsal sistemler için, statik kod analizi hem de etki analizi Aynı mantıksal hesaplamanın farklı dillerde farklı biçimlerde nerede göründüğünü belirlemek; bu, Replace Temp with Query'nin tek dil düzeyinde ele aldığı sorunun daha derin biçimidir.

Üzerinde çalışan ekipler için miras modernizasyonu, SMART TS XL'S bağımlılık görselleştirmesi Bu sayede, yeniden düzenlenmiş bileşenlerin nerede kullanıldığını, değişiklik yapılmadan önce görmek mümkün olur ve bir hesaplamanın sorgu yöntemine çıkarılmasının, orijinal yapıyı bekleyen çağırıcıları bozmaması sağlanır.

Geçici Değişkenler ve Kendini Belgeleyen Kod

Geçici bir değişkeni sorguyla değiştirme kararı, nihayetinde kodun neyi iletmesi gerektiğiyle ilgili bir karardır. Geçici değişkenler uygulama bilgisini iletir: bu değeri elde etmek için yaptığım hesaplama budur. Sorgu yöntemleri etki alanını iletir: bu değerin anlamı budur. Bir Sipariş sınıfında, basePrice() Bu, okuyucuya bu kavramın ilgili alanda mevcut olduğunu bildirir. double x = quantity * itemPrice Okuyucuya bir aritmetik işlemi anlatır.

Kod geliştikçe, alan kavramlarının istikrarlı yerlere ihtiyacı vardır. Geçici bir değişkene yerleştirilmiş bir hesaplama değişebilir, birden fazla metotta tekrarlanabilir veya onu okuyan bir sonraki geliştirici tarafından yanlış anlaşılabilir. Adlandırılmış bir sorgu metodu ise bulunabilir, test edilebilir, belgelenebilir ve kasıtlı olarak geliştirilebilir. Hesaplamanın gerekli olduğu tüm yerlerde ve onunla çalışacak tüm geliştiricilerde bu istikrar, "Geçici Değişkeni Sorgu ile Değiştir" seçeneğini bir sözdizimi değişikliğinden daha fazlası yapar. Bu, kod tabanının çözdüğü problemi nasıl ilettiğiyle ilgili bir karardır.