Dinamik kapsama sahip bir dilde nasıl güvenli bir şekilde yeniden düşünürsünüz?


13

Dinamik kapsamda bir dilde çalışmama şansına sahip olanlar için, bunun nasıl çalıştığı hakkında size biraz bilgi vereyim. "RUBELLA" adlı sahte bir dil düşünün:

function foo() {
    print(x); // not defined locally => uses whatever value `x` has in the calling context
    y = "tetanus";
}
function bar() {
    x = "measles";
    foo();
    print(y); // not defined locally, but set by the call to `foo()`
}
bar(); // prints "measles" followed by "tetanus"

Yani, değişkenler çağrı yığınını yukarı ve aşağı serbestçe yayar - içinde tanımlanan tüm değişkenler fooarayan tarafından görülebilir (ve değiştirilebilir) barve bunun tersi de doğrudur. Bunun kodun yeniden düzenlenebilirliği üzerinde ciddi etkileri vardır. Aşağıdaki koda sahip olduğunuzu düşünün:

function a() { // defined in file A
    x = "qux";
    b();
}
function b() { // defined in file B
    c();
}
function c() { // defined in file C
    print(x);
}

Şimdi, aramalar a()yazdırılacak qux. Ama sonra, bir gün, bbiraz değişmen gerektiğine karar verdin . Tüm arama bağlamlarını bilmiyorsunuz (bazıları kod tabanınızın dışında olabilir), ancak bu iyi olmalı - değişiklikleriniz tamamen içsel olacak b, değil mi? Yani şöyle yeniden yazıyorsunuz:

function b() {
    x = "oops";
    c();
}

Ve yerel bir değişken tanımladığınız için hiçbir şeyi değiştirmediğinizi düşünebilirsiniz. Ama aslında, kırıldın a! Şimdi, abaskılar oopsyerine qux.


Bunu sahte diller alanından geri getirmek, farklı sözdizimiyle de olsa MUMPS'un tam olarak böyle davrandığıdır.

MUMPS'un modern ("modern") sürümleri, NEWdeğişkenlerin bir çağrıdan arayan kişiye sızmasını önlemenizi sağlayan deyim içerir . Yaptığımız olsaydı Yani yukarıdaki ilk örnekte, NEW y = "tetanus"içinde foo(), daha sonra print(y)içinde bar()(açıkça başka bir şeye ayarlı sürece Kabakulak içinde, tüm isimler boş bir dizeye işaret) hiçbir şey basacaktır. Ancak değişkenlerin bir arayandan bir arayana sızmasını önleyebilecek hiçbir şey yoktur: function p() { NEW x = 3; q(); print(x); }bildiğimiz kadarıyla, açıkça bir parametre olarak almamasına rağmen , q()mutasyon geçirebilirsek . Bu hala içinde olmak kötü bir durum, ama muhtemelen eskisi kadar kötü değil .xx

Bu tehlikeleri göz önünde bulundurarak, MUMPS veya dinamik kapsam belirleme özelliğine sahip başka bir dilde kodu nasıl güvenle yeniden düzenleyebiliriz?

Yeniden düzenlemeyi kolaylaştırmak için, NEWkendiniz başlattığınız ( ) dışında bir işlevde değişkenleri asla kullanmamak veya açık bir parametre olarak iletmek ve bir işlevin arayanlarından dolaylı olarak geçirilen parametreleri açıkça belgelemek gibi bazı belirgin iyi uygulamalar vardır . Ama onlarca yaşında, ~ 10 8 -LOC kod temeli, bunlar genellikle sahip olmadığı lükslerdir.

Ve elbette, temel olarak sözlük kapsamı olan dillerde yeniden düzenleme için tüm iyi uygulamalar, dinamik kapsam - yazma testleri vb. Dillerde de uygulanabilir. Öyleyse soru şu: dinamik olarak kapsamlı kodun kırılganlığının yeniden düzenlenmesi sırasında özel olarak ortaya çıkardığı riskleri nasıl azaltıyoruz?

( Dinamik bir dilde yazılan kodda nasıl gezinir ve yeniden düzenleme yapıyorsunuz? Bu soruya benzer bir başlık olsa da, bunun tamamen ilgisiz olduğunu unutmayın.)



@gnat Bu sorunun / cevaplarının bu soru ile ne kadar alakalı olduğunu görmüyorum.
senshin

1
@gnat Cevabın "farklı süreçler ve diğer ağır malzemeler kullanmak" olduğunu mu söylüyorsunuz? Demek istediğim, bu muhtemelen yanlış değil, ama aynı zamanda özellikle yararlı olmama noktasında da genel.
senshin

2
Dürüst olmak gerekirse, bunun "değişkenlerin kapsam belirleme kurallarına sahip olduğu bir dile geç" veya "her bir değişkenin dosya ve / veya yöntem adıyla öneki olduğu Macar gösteriminin piç stepchild'ini kullanmanın dışında bir cevap olduğunu düşünmüyorum. "tür veya türden". Açıkladığınız sorun o kadar korkunç ki iyi bir çözüm hayal edemiyorum .
Ixrec

4
En azından pislik hastalığının ismiyle MUMPS'i yanlış reklamlarla suçlayamazsınız.
Carson63000

Yanıtlar:


4

Vay.

MUMPS'u dil olarak bilmiyorum, bu yüzden yorumumun burada geçerli olup olmadığını bilmiyorum. Genel olarak konuşursak - İçten dışa refactor gerekir. Küresel devletin bu tüketicileri (okuyucuları) (küresel değişkenler) parametreler kullanılarak yöntemlere / fonksiyonlara / prosedürlere dönüştürülmelidir. Yeniden düzenleme işleminden sonra c yöntemi şöyle görünmelidir:

function c(c_scope_x) {
   print c(c_scope_x);
}

c'nin tüm kullanımları yeniden yazılmalıdır (mekanik bir görevdir)

c(x)

bu, yerel durumu kullanarak "iç" kodu küresel durumdan izole etmektir. Bununla işiniz bittiğinde, b'yi aşağıdakilere yeniden yazmanız gerekir:

function b() {
   x="oops"
   print c(x);
}

x = "oops" ataması yan etkileri korumak için vardır. Şimdi b'yi küresel devleti kirletici olarak düşünmeliyiz. Yalnızca bir kirli öğeniz varsa bu yeniden düzenlemeyi düşünün:

function b() {
   x="oops"
   print c(x);
   return x;
}

b'nin her kullanımını x = b () ile yeniden yazın. İşlev b, bu yeniden düzenleme işlemini gerçekleştirirken yalnızca temizlenmiş yöntemleri kullanmalıdır (yeniden adlandırmayı netleştirebilirsiniz). Bundan sonra, küresel çevreyi kirletmemek için b'yi yeniden gözden geçirmelisiniz.

function b() {
   newvardefinition b_scoped_x="oops"
   print c_cleaned(b_scoped_x);
   return b_scoped_x;
}

b'yi b_cleaned olarak yeniden adlandırın. Sanırım bu yeniden düzenlemeye alışmak için biraz oynamak zorunda kalacaksınız. Tabii ki her yöntem bununla yeniden düzenlenemez, ancak iç kısımlardan başlamak zorunda kalacaksınız. Bir fikir edinmek için Eclipse ve java (özüt yöntemleri) ve "küresel devlet" aka sınıf üyeleri ile deneyin.

function x() {
  fifth_to_refactor();
  {
    forth_to_refactor()
    ....
    {
      second_to_refactor();
    }
    ...
    third_to_refactor();
  }
  first_to_refactor()
}

hth.

Soru: Bu tehlikeleri göz önünde bulundurarak, MUMPS veya dinamik kapsam belirleme özelliğine sahip başka bir dilde kodu nasıl güvenle yeniden düzenleyebiliriz?

  • Belki başkası bir ipucu verebilir.

Soru: Yeniden düzenleme yaparken dinamik olarak kapsamlandırılmış kodun artan kırılganlığı ile özellikle ilişkili riskleri nasıl azaltabiliriz?

  • Sizin için güvenli yeniden düzenlemeleri yapan bir program yazın.
  • Güvenli adayları / ilk adayları tanımlayan bir program yazın.

Ah, yeniden düzenleme sürecini otomatikleştirmeye çalışmak için MUMPS'a özgü bir engel var: MUMPS birinci sınıf fonksiyonlara sahip değil, fonksiyon göstergeleri veya benzer bir fikre sahip değil. Bu, herhangi bir büyük MUMPS kod tabanının kaçınılmaz olarak , bazen dezenfekte edilmiş kullanıcı girişinde bile, çok sayıda değerlendirme kullanımına sahip olacağı EXECUTEanlamına gelir - bu, bir işlevin tüm kullanımlarını statik olarak bulmanın ve yeniden yazmanın imkansız olduğu anlamına gelir.
senshin

Tamam, cevabımı yeterli bulmuyorum. Bence refactoring @ google ölçeğinde bir youtube video çok benzersiz bir yaklaşım yaptı. Bir AST'yi ayrıştırmak için clang kullandılar ve daha sonra kodlarını yeniden düzenlemek için herhangi bir (hatta gizli kullanım) bulmak için kendi arama motorlarını kullandılar. Bu, her kullanımı bulmanın bir yolu olabilir. Kabakulak kodunda ayrıştırma ve arama yaklaşımı demek istiyorum.
thepacker

2

Sanırım en iyi çekiminiz tam kod tabanını kontrolünüz altına almak ve modüller ve bağımlılıkları hakkında bir genel bakışa sahip olduğunuzdan emin olmaktır.

Yani en azından küresel arama yapma şansınız var ve sistemin bir kod değişikliğinden etkilenmeyi beklediğiniz kısımları için regresyon testleri ekleme şansınız var.

İlkini gerçekleştirme şansı görmüyorsanız, en iyi tavsiyem: diğer modüller tarafından yeniden kullanılan veya başkalarının bunlara bağlı olduğunu bilmediğiniz modülleri yeniden düzenlemeyin . Makul boyuttaki herhangi bir kod tabanında şansı yüksektir, başka hiçbir modülün bağlı olmadığı modülleri bulabilirsiniz. Dolayısıyla, B'ye bağlı bir mod A'ya sahipseniz, bunun tersi değilse ve dinamik olarak kapsamlandırılmış bir dilde bile başka hiçbir modül A'ya bağlı değilse, B'de veya diğer modüllerde bozulmadan A'da değişiklik yapabilirsiniz.

Bu, A'dan B'ye bağımlılığı A'nın B2'ye bağımlılığı ile değiştirme şansı verir; burada B2, B'nin sterilize edilmiş, yeniden yazılmış bir versiyonudur. B2, kodu yapmak için yukarıda bahsettiğiniz kurallar dikkate alınarak yeni bir şekilde yazılmalıdır. daha evrim geçirebilir ve yeniden düzenlenebilir daha kolay.


Bu, iyi bir tavsiyedir, ancak bir kenara ekleyeceğim, çünkü erişim belirteçleri veya başka bir kapsülleme mekanizması kavramı olmadığı için bu, MUMPS'de doğal olarak zordur, yani kod tabanımızda belirttiğimiz API'lerin tüketicilere hangi işlevleri çağırmaları gerektiği hakkında kod . (Elbette, bu özel zorluk dinamik kapsam belirleme ile ilgisizdir; Bunu sadece bir ilgi noktası olarak not ediyorum.)
senshin senshin

Bu makaleyi okuduktan sonra , sizi göreviniz için kıskanmayacağımdan eminim.
Doc Brown

0

Açık olanı belirtmek için: Burada yeniden düzenleme nasıl yapılır? Çok dikkatli olun.

(Açıkladığınız gibi, mevcut kod tabanını geliştirmek ve sürdürmek, yeniden düzenlemeye çalışmak yerine, yeterince zor olmalıdır.)

Burada geriye dönük olarak test odaklı bir yaklaşım uygulayacağım. Bu, yeniden düzenlemeye başladığınızda, ilk olarak yalnızca testi kolaylaştırmak için mevcut işlevselliğin çalışmaya devam etmesini sağlamak için bir dizi test yazmayı içerir. (Evet, kodunuz hiç değiştirmeden test etmek için yeterince modüler değilse, burada bir tavuk ve yumurta sorunu bekliyorum.)

Ardından, devam ederken herhangi bir testi kırmadığınızı kontrol ederek diğer yeniden düzenleme işlemlerine devam edebilirsiniz.

Son olarak, yeni işlevsellik bekleyen testler yazmaya başlayabilir ve daha sonra bu testlerin çalışması için kodu yazabilirsiniz.

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.