คุณจะสร้างโครงสร้างใหม่อย่างปลอดภัยในภาษาที่มีขอบเขตแบบไดนามิกได้อย่างไร


13

สำหรับพวกคุณที่โชคดีที่ไม่ได้ทำงานในภาษาที่มีขอบเขตแบบไดนามิกให้ฉันขอทบทวนเล็กน้อยเกี่ยวกับวิธีการทำงาน ลองนึกภาพภาษาเทียมที่เรียกว่า "RUBELLA" ซึ่งมีลักษณะดังนี้:

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"

นั่นคือตัวแปรแพร่กระจายขึ้นและลงสแต็กการโทรได้อย่างอิสระ - ตัวแปรทั้งหมดที่กำหนดในfooนั้นสามารถมองเห็นได้ (และกลายพันธุ์โดย) ผู้โทรของมันbarและสิ่งที่ตรงกันข้ามก็เป็นจริงเช่นกัน สิ่งนี้มีความหมายอย่างร้ายแรงต่อการ refactorability ของรหัส ลองนึกภาพว่าคุณมีรหัสต่อไปนี้:

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

ตอนนี้โทรไปจะพิมพ์a() quxแต่สักวันคุณตัดสินใจว่าคุณต้องเปลี่ยนbนิดหน่อย คุณไม่รู้บริบทการโทรทั้งหมด (ซึ่งบางอันอาจอยู่นอก codebase ของคุณ) แต่มันก็ไม่เป็นไรการเปลี่ยนแปลงของคุณจะต้องอยู่ภายในอย่างสมบูรณ์bใช่มั้ย ดังนั้นคุณเขียนมันใหม่แบบนี้:

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

และคุณอาจคิดว่าคุณไม่ได้เปลี่ยนแปลงอะไรเลยเนื่องจากคุณเพิ่งกำหนดตัวแปรท้องถิ่น แต่ที่จริงแล้วคุณเสียแล้วa! ตอนนี้aพิมพ์มากกว่าoopsqux


การนำสิ่งนี้กลับออกมาจากขอบเขตของภาษาเทียมนี่คือสิ่งที่ MUMPS ทำงานแม้ว่าจะมีไวยากรณ์ที่แตกต่างกัน

MUMPS รุ่น Modern ("modern") รวมNEWคำสั่งที่เรียกว่าซึ่งช่วยให้คุณสามารถป้องกันตัวแปรจากการรั่วไหลของผู้โทรไปยังผู้โทร ดังนั้นในตัวอย่างแรกข้างต้นถ้าเราได้ทำNEW y = "tetanus"ในfoo()แล้วprint(y)ในbar()จะพิมพ์อะไร (ในคางทูม, ชื่อทั้งหมดชี้ไปที่สตริงว่างเว้นแต่กำหนดอย่างชัดเจนเป็นอย่างอื่น) แต่ไม่มีอะไรที่สามารถป้องกันตัวแปรจากการรั่วไหลของผู้โทรไปยังผู้ถูก: ถ้าเรามีfunction p() { NEW x = 3; q(); print(x); }สำหรับสิ่งที่เรารู้ว่าq()สามารถกลายพันธุ์xแม้จะไม่ได้รับอย่างชัดเจนxเป็นพารามิเตอร์ นี้ยังคงเป็นสถานการณ์ที่เลวร้ายที่จะอยู่ใน แต่ไม่เป็นไม่ดีเท่าที่มันอาจเคยเป็น

เมื่อทราบถึงอันตรายเหล่านี้แล้วเราจะสร้างรหัสอย่างปลอดภัยใน MUMPS หรือภาษาอื่นที่มีการกำหนดขอบเขตแบบไดนามิกได้อย่างไร

มีบางปฏิบัติที่ดีอย่างเห็นได้ชัดสำหรับการทำ refactoring ง่ายขึ้นอย่างที่ไม่เคยใช้ตัวแปรในฟังก์ชั่นอื่น ๆ ที่นอกเหนือจากที่คุณเริ่มต้น (มีNEWหรือ) ตัวเองจะถูกส่งผ่านเป็นพารามิเตอร์ที่ชัดเจนและการจัดเก็บเอกสารอย่างชัดเจนพารามิเตอร์ใด ๆ ที่จะผ่านไปโดยปริยายจากสายที่ฟังก์ชั่นของ แต่ในทศวรรษที่ผ่านมา ~ 10 8 -LOC codebase เหล่านี้เป็นของฟุ่มเฟือยที่มักจะไม่มี

และแน่นอนว่าหลักปฏิบัติที่ดีทั้งหมดสำหรับการปรับโครงสร้างใหม่ในภาษาที่มีขอบเขตศัพท์ยังสามารถใช้ได้ในภาษาที่มีขอบเขตแบบไดนามิก - การทดสอบการเขียนและอื่น ๆ จากนั้นคำถามก็คือสิ่งนี้: เราจะลดความเสี่ยงที่เกี่ยวข้องโดยเฉพาะกับความเปราะบางที่เพิ่มขึ้นของโค้ดที่กำหนดขอบเขตแบบไดนามิกเมื่อทำการปรับโครงสร้างใหม่ได้อย่างไร?

(โปรดทราบว่าในขณะที่คุณนำทางและ refactor รหัสที่เขียนในภาษาแบบไดนามิกได้อย่างไรมีชื่อที่คล้ายกันกับคำถามนี้มันไม่เกี่ยวข้องทั้งหมด)



@gnat ฉันไม่เห็นว่าคำถาม / คำตอบนั้นเกี่ยวข้องกับคำถามนี้อย่างไร
senshin

1
@gnat คุณกำลังบอกว่าคำตอบคือ "ใช้กระบวนการที่แตกต่างกันและสิ่งที่เฮฟวี่เวทอื่น ๆ "? ฉันหมายความว่านั่นอาจไม่ผิด แต่ก็เกินธรรมดาไปจนถึงจุดที่ไม่มีประโยชน์อย่างยิ่ง
senshin

2
สุจริตฉันไม่คิดว่าจะมีคำตอบนี้นอกเหนือจาก "เปลี่ยนเป็นภาษาที่ตัวแปรมีกฎการกำหนดขอบเขตจริง ๆ " หรือ "ใช้ลูกนอกสมรสของลูกครึ่งฮังการีของสัญกรณ์ฮังการีที่ทุกตัวแปรมีคำนำหน้าโดยไฟล์และ / หรือชื่อวิธีการแทน กว่าชนิดหรือชนิด " ปัญหาที่คุณอธิบายนั้นแย่มากฉันไม่สามารถจินตนาการถึงทางออกที่ดีได้
Ixrec

4
อย่างน้อยที่สุดคุณก็ไม่สามารถกล่าวหา MUMPS ของการโฆษณาที่ผิด ๆ ว่าได้รับการตั้งชื่อตามโรคร้าย
Carson63000

คำตอบ:


4

ว้าว.

ฉันไม่ทราบว่า MUMPS เป็นภาษาหนึ่งดังนั้นฉันไม่ทราบว่าความคิดเห็นของฉันมีผลกับที่นี่หรือไม่ โดยทั่วไปการพูด - คุณต้อง refactor จากภายในสู่ภายนอก ผู้บริโภค (ผู้อ่าน) ของรัฐทั่วโลก (ตัวแปรทั่วโลก) จะต้องได้รับการ refactored เป็นวิธีการ / ฟังก์ชั่น / ขั้นตอนการใช้พารามิเตอร์ วิธี c ควรมีลักษณะเช่นนี้หลังจาก refactoring:

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

การเขียน c ทั้งหมดต้องถูกเขียนใหม่ (ซึ่งเป็นงานเชิงกล)

c(x)

นี่คือการแยกรหัส "Inner" ออกจากสถานะโกลบอลโดยใช้สถานะโลคัล เมื่อคุณทำเสร็จแล้วคุณจะต้องเขียนขใหม่เป็น:

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

การมอบหมาย x = "oops" อยู่ที่นั่นเพื่อรักษาผลข้างเคียง ตอนนี้เราต้องพิจารณาขว่าจะก่อให้เกิดมลพิษต่อรัฐโลก หากคุณมีองค์ประกอบที่มีมลพิษเพียงองค์ประกอบเดียวให้พิจารณาการเปลี่ยนโครงสร้างนี้:

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

end เขียนใหม่การใช้ b แต่ละครั้งด้วย x = b () ฟังก์ชั่น b ต้องใช้วิธีการที่ได้รับการล้างข้อมูลแล้วเท่านั้น (คุณอาจต้องการให้เปลี่ยนชื่อ co ให้ชัดเจน) เมื่อทำการปรับโครงสร้างนี้ หลังจากนั้นคุณควร refactor b เพื่อไม่ก่อให้เกิดมลพิษต่อสิ่งแวดล้อมโลก

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

เปลี่ยนชื่อ b เป็น b_cleaned ฉันเดาว่าคุณจะต้องเล่นกับสิ่งนั้นเพื่อให้ได้เสียงที่ดีขึ้น แน่นอนว่าไม่ใช่ทุกวิธีที่จะได้รับการปรับปรุงใหม่โดยวิธีนี้ แต่คุณจะต้องเริ่มจากส่วนด้านใน ลองใช้ด้วย Eclipse และ java (แยกวิธีการ) และ "สมาชิกระดับโลก" หรือสมาชิกชั้นเรียนเพื่อรับแนวคิด

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

HTH

คำถาม: เมื่อคำนึงถึงอันตรายเหล่านี้แล้วเราจะสร้างรหัสอย่างปลอดภัยใน MUMPS หรือภาษาอื่นที่มีการกำหนดขอบเขตแบบไดนามิกได้อย่างไร

  • บางทีคนอื่นอาจให้คำใบ้

คำถาม: เราจะลดความเสี่ยงที่เกี่ยวข้องโดยเฉพาะกับความเปราะบางที่เพิ่มขึ้นของโค้ดที่กำหนดขอบเขตแบบไดนามิกเมื่อทำการปรับโครงสร้างใหม่ได้อย่างไร?

  • เขียนโปรแกรมซึ่งจะทำการปรับสภาพที่ปลอดภัยสำหรับคุณ
  • เขียนโปรแกรมซึ่งระบุผู้สมัครที่ปลอดภัย / ผู้สมัครคนแรก

อามีอุปสรรคหนึ่งที่เฉพาะเจาะจงของ MUMPS ในการพยายามทำให้กระบวนการเปลี่ยนโครงสร้างเป็นอัตโนมัติ: MUMPS ไม่มีฟังก์ชั่นชั้นหนึ่งและไม่มีพอยน์เตอร์ของฟังก์ชั่น ซึ่งหมายความว่าคางทูมขนาดใหญ่ใด ๆ codebase ย่อมจะมีจำนวนมากของการใช้งานของ EVAL (ในคางทูมเรียกว่าEXECUTE) แม้บางครั้งที่ผู้ใช้ป้อนสุขอนามัย - ซึ่งหมายความว่ามันสามารถเป็นไปไม่ได้ที่จะหาแบบคงที่และเขียนประเพณีทั้งหมดของฟังก์ชั่น
senshin

โอเคพิจารณาคำตอบของฉันไม่เพียงพอ วิดีโอ youtube ฉันคิดว่า refactoring @ google scale มีวิธีที่ไม่เหมือนใคร พวกเขาใช้เสียงดังกราวเพื่อแยก AST แล้วใช้เครื่องมือค้นหาของตนเองเพื่อค้นหา (แม้แต่การใช้งานที่ซ่อนอยู่) เพื่อสร้างรหัสของพวกเขาอีกครั้ง นี่อาจเป็นวิธีในการค้นหาการใช้งานทุกอย่าง ฉันหมายถึงการแยกวิเคราะห์และวิธีการค้นหาในรหัสคางทูม
thepacker

2

ฉันเดาว่าช็อตที่ดีที่สุดของคุณคือการนำเอาโค้ดแบบเต็มภายใต้การควบคุมของคุณและตรวจสอบให้แน่ใจว่าคุณมีภาพรวมเกี่ยวกับโมดูลและการพึ่งพา

อย่างน้อยคุณก็มีโอกาสทำการค้นหาทั่วโลกและมีโอกาสเพิ่มการทดสอบการถดถอยสำหรับส่วนต่าง ๆ ของระบบที่คุณคาดว่าจะได้รับผลกระทบจากการเปลี่ยนรหัส

หากคุณไม่เห็นโอกาสที่จะประสบความสำเร็จเป็นครั้งแรกที่แนะนำที่ดีที่สุดของฉันคือไม่ refactor โมดูลใด ๆ ที่จะนำกลับมาใช้โดยโมดูลอื่น ๆ หรือที่คุณไม่ทราบว่าคนอื่น ๆ พึ่งพาพวกเขา ใน codebase ใด ๆ ที่มีขนาดที่เหมาะสมมีโอกาสสูงที่คุณจะพบโมดูลที่ไม่มีโมดูลอื่น ๆ ดังนั้นหากคุณมี mod A ขึ้นอยู่กับ B แต่ไม่ใช่ในทางกลับกันและไม่มีโมดูลอื่นขึ้นอยู่กับ A แม้ในภาษาที่กำหนดขอบเขตแบบไดนามิกคุณสามารถทำการเปลี่ยนแปลง A ได้โดยไม่ทำให้ B หรือโมดูลอื่น ๆ แตกหัก

สิ่งนี้ให้โอกาสคุณในการแทนที่การพึ่งพา A ถึง B โดยการพึ่งพา A ถึง B2 โดยที่ B2 เป็นรุ่น B2 ที่ถูกสุขลักษณะที่ถูกเขียนใหม่ของ B. B2 ควรเป็นลายลักษณ์อักษรใหม่โดยมีกฎที่คุณกล่าวถึงข้างต้นเพื่อสร้างรหัส evolvable มากขึ้นและง่ายต่อการ refactor


นี่เป็นคำแนะนำที่ดีแม้ว่าฉันจะเพิ่มว่านี่เป็นเรื่องยากใน MUMPS เนื่องจากไม่มีแนวคิดเกี่ยวกับตัวระบุการเข้าถึงหรือกลไกการห่อหุ้มอื่น ๆ ซึ่งหมายความว่า API ที่เราระบุใน codebase ของเราเป็นเพียงคำแนะนำแก่ผู้บริโภค รหัสเกี่ยวกับฟังก์ชั่นที่พวกเขาควรจะเรียก (แน่นอน
ว่าความ

หลังจากอ่านบทความนี้ฉันแน่ใจว่าฉันไม่อิจฉาคุณสำหรับงานของคุณ
Doc Brown

0

เมื่อต้องการระบุสิ่งที่ชัดเจน: จะทำการปรับโครงสร้างที่นี่ได้อย่างไร ดำเนินการอย่างระมัดระวัง

(ดังที่คุณได้อธิบายไว้การพัฒนาและบำรุงรักษาฐานรหัสที่มีอยู่ควรจะยากพอให้พยายามปรับโครงสร้างอีกครั้งโดยลำพัง)

ฉันเชื่อว่าฉันจะใช้วิธีทดสอบด้วยการผลักดันย้อนหลังที่นี่ สิ่งนี้จะเกี่ยวข้องกับการเขียนชุดการทดสอบเพื่อให้แน่ใจว่าฟังก์ชั่นปัจจุบันยังคงใช้งานได้ในขณะที่คุณเริ่มการปรับโครงสร้างครั้งแรกเพียงเพื่อให้การทดสอบง่ายขึ้น (ใช่ฉันคาดว่าจะมีปัญหาไก่และไข่ที่นี่ยกเว้นว่าโค้ดของคุณเป็นโมดูลที่เพียงพอสำหรับการทดสอบโดยไม่ต้องเปลี่ยนเลย)

จากนั้นคุณสามารถดำเนินการซ่อมแซมต่ออีกครั้งโดยตรวจสอบว่าคุณไม่ได้ทำการทดสอบใด ๆ ที่เสียหาย

สุดท้ายคุณสามารถเริ่มเขียนการทดสอบที่คาดว่าจะมีฟังก์ชั่นใหม่แล้วเขียนรหัสเพื่อให้การทดสอบเหล่านั้นทำงาน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.