แก้ไขคําจํากัดความของบล็อก

คำถามที่พบบ่อยคือวิธีแก้ไขคําจํากัดความของบล็อกที่มีอยู่ เช่น คุณอาจต้องการเพิ่มการตรวจสอบการเชื่อมต่อหรือเปลี่ยนช่องเป็นอินพุตค่า

โดยทั่วไปแล้ว คุณจะแก้ไขคําจํากัดความของบล็อกไม่ได้

วิธีแก้ไขคําจํากัดความที่มีอยู่

หากต้องการแก้ไขคําจํากัดความของประเภทบล็อกที่มีอยู่ ให้ทําดังนี้

  1. ทำสำเนาของคำจำกัดความที่มีอยู่ รวมถึงเครื่องมือสร้างโค้ดบล็อก
  2. แก้ไขข้อความและตั้งชื่อใหม่ให้ประเภท
  3. เพิ่มคําจํากัดความใหม่ลงใน Blockly.Blocks

ทำไมฉันจึงแก้ไขคำจำกัดความที่มีอยู่โดยตรงไม่ได้

หากสงสัยว่าเหตุใดคุณจึงแก้ไขคําจํากัดความที่มีอยู่ไม่ได้ โปรดอ่านต่อ เราจะพิจารณาความเป็นไปได้บางอย่าง

แก้ไขคําจํากัดความที่มีอยู่โดยตรง

การแก้ไขคําจํากัดความของบล็อกที่มีอยู่โดยตรงทำได้ 2 วิธี ได้แก่ การแก้ไขโค้ดโดยตรง (Monkey Patching) และการแยกโค้ด เราขอแนะนำอย่างยิ่งว่าอย่าใช้ทั้ง 2 วิธีนี้ เนื่องจากคุณอาจเสี่ยงที่จะทำให้โค้ดที่ใช้โค้ดที่แก้ไขด้วย Monkey Patch หรือโค้ดที่แยกออกมาใช้งานไม่ได้ ทั้ง 2 เทคนิคยังทําให้ผสานรวมการอัปเดตและการแก้ไขข้อบกพร่องได้ยากด้วย ดูข้อมูลเพิ่มเติมได้ที่Monkeypatching เป็นอย่างไรและการแยก Blockly

กำหนดคลาสย่อยของคำจำกัดความที่มีอยู่

คุณอาจต้องการสร้างคลาสย่อยของคำจำกัดความที่มีอยู่ ขออภัย การดำเนินการนี้ไม่สามารถทำได้เนื่องจากคําจํากัดความของบล็อกไม่ใช่คลาส แต่เป็นมิกซ์อิน เช่น คุณจะลบล้างพร็อพเพอร์ตี้สีไม่ได้เนื่องจากคำจำกัดความไม่มีพร็อพเพอร์ตี้สี แต่มีฟังก์ชัน init ที่เรียก setColour เพื่อตั้งค่าพร็อพเพอร์ตี้สีในบล็อกแทน เนื่องจากการเรียกใช้อยู่ภายใน init จึงไม่มีวิธีแทนที่การเรียกใช้ดังกล่าวโดยไม่แทนที่ฟังก์ชัน init ทั้งหมด

เขียนทับฟังก์ชันในคําจํากัดความที่มีอยู่

คุณสามารถเขียนทับฟังก์ชันในคําจํากัดความที่มีอยู่ได้ โดยทําดังนี้

Blockly.Blocks['existing_block'].init = function() {/*new function*/};

วิธีนี้ใช้ได้ แต่คุณต้องคัดลอกและแก้ไขฟังก์ชันที่มีอยู่ ไม่สามารถแทนที่เพียง 1 บรรทัดได้ การดำเนินการนี้มีปัญหาหลายประการ ดังนี้

  • ซึ่งไม่ต่างกับการคัดลอกและแก้ไขคําจํากัดความทั้งฉบับ
  • คุณใช้เพื่อแก้ไขฟังก์ชัน init ของบล็อกที่กําหนดไว้ใน JSON ไม่ได้ เช่น บล็อกในตัวของ Blockly เนื่องจากไม่มีinit ฟังก์ชันที่จะคัดลอก ฟังก์ชันดังกล่าวสร้างขึ้นที่รันไทม์
  • โดยคุณจะต้องกำหนดบล็อกโดยใช้ JavaScript ซึ่งอาจทำให้เกิดปัญหาเกี่ยวกับการแปล

เขียนทับผลลัพธ์ของ init

วิธี "แทนที่เพียง 1 บรรทัด" ของฟังก์ชัน init คือแทนที่ฟังก์ชัน init ด้วยฟังก์ชันที่เรียกใช้ฟังก์ชัน init เดิม แล้วเขียนทับผลลัพธ์ของการเรียกใช้นั้น ตัวอย่างเช่น โค้ดต่อไปนี้จะเปลี่ยนสีของบล็อก logic_null

const originalInit = Blockly.Blocks['logic_null'].init;
Blockly.Blocks['logic_null'].init = function() {
  originalInit.call(this);
  this.setColour(300);
}

แต่น่าเสียดายที่วิธีนี้ไม่ค่อยมีประโยชน์เท่าที่ควร เช่น

  • การขยายการตรวจสอบการเชื่อมต่อหรือการใช้โปรแกรมตรวจสอบฟิลด์ที่เข้มงวดน้อยลงอาจทำให้สมมติฐานที่สร้างขึ้นโดยเครื่องมือสร้างโค้ดบล็อกและตัวแฮนเดิลเหตุการณ์เป็นโมฆะ

  • การแทนที่ช่องด้วยอินพุตค่าจะทําให้เครื่องมือสร้างโค้ดบล็อกและโปรแกรมตรวจสอบช่องใช้งานไม่ได้ และอาจทําให้ตัวแฮนเดิลเหตุการณ์ใช้งานไม่ได้ นอกจากนี้ ยังอาจทําได้ยากมากสําหรับบล็อกที่แปลแล้ว เนื่องจากภาษาที่ต่างกันอาจส่งผลให้บล็อกมีประเภทและลําดับของอินพุตและช่องที่แตกต่างกัน

เขียนทับคู่คีย์-ค่าในคําจํากัดความ JSON

หาก JSON ของบล็อกมีให้บริการแบบสาธารณะ คุณอาจเขียนทับค่า JSON แต่ละรายการได้ เช่น

// Block definition.
blockJson = {...};
Blockly.Blocks['my_block'] = {
  init: function() {
    initJson(blockJson); // Called when the block is created.
  }
}

// Third-party code.
blockJson.colour = 100;

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

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

// Doesn't work. There is no getJson() function.
const json = Blockly.Blocks['existing_block'].getJson();
json['message0'] = 'my new message0';
Blockly.Blocks['existing_block'].init = function () {
  initJson(json);
};

ออกแบบเพื่อใช้ซ้ำ

เมื่อออกแบบบล็อกที่กำหนดเอง คุณอาจออกแบบบล็อกในลักษณะที่ส่งเสริมการนำกลับมาใช้ซ้ำได้

ใช้ JSON ซ้ำ

หากมีบล็อก 2 รายการที่คล้ายกันมาก คุณสามารถสร้างคําจํากัดความ JSON หลักและนําไปใช้ซ้ำในคําจํากัดความย่อยได้ เช่น

const parentJson = {
  // shared properties
};

Blockly.Blocks['child_block_1'] = {
  init: function() {
    initJson({...parentJson, colour: 100})
  }
}

Blockly.Blocks['child_block_2'] = {
  init: function() {
    initJson({...parentJson, colour: 200})
  }
}

อีกทางเลือกหนึ่งคือกําหนด JSON ของคุณในออบเจ็กต์ที่เผยแพร่ต่อสาธารณะ และส่งออบเจ็กต์นั้นไปยัง initJson ในฟังก์ชัน init ซึ่งทำให้ผู้อื่นเขียนทับพร็อพเพอร์ตี้แต่ละรายการได้ ดูข้อมูลเพิ่มเติมได้ที่เขียนทับคู่คีย์-ค่าในการคําจํากัดความ JSON

ใช้ฟังก์ชันซ้ำ

บล็อกอาจกำหนดฟังก์ชันมาตรฐานจำนวนหนึ่ง เช่น ตัวแฮนเดิลเหตุการณ์ระดับบล็อก เคล็ดลับเครื่องมือที่กำหนดเอง ตัวตรวจสอบฟิลด์ และฟังก์ชันที่ตัวเปลี่ยนรูปแบบใช้ รวมถึงฟังก์ชันที่ให้ลักษณะการทำงานที่กำหนดเอง เช่น ฟังก์ชันที่ตั้งค่าฟิลด์จากข้อมูลภายนอก เช่น ตำแหน่งปัจจุบันของแขนหุ่นยนต์

คุณอาจนำฟังก์ชันเหล่านี้ไปใช้ซ้ำในบล็อกต่างๆ ได้

ใช้ช่องเมนูแบบเลื่อนลง

หากมีชุดบล็อกที่เหมือนกันเกือบทั้งหมดยกเว้นโอเปอเรเตอร์ คุณอาจออกแบบบล็อกเดียวที่มีช่องแบบเลื่อนลงสำหรับโอเปอเรเตอร์ได้ เช่น

  • บล็อก logic_operation ในตัวใช้เมนูแบบเลื่อนลงที่มีโอเปอเรเตอร์ and และ or
  • บล็อก math_arithmetic ในตัวใช้เมนูแบบเลื่อนลงที่มีโอเปอเรเตอร์ +, -, ×, ÷ และ ^

โดยทั่วไปการเขียนเครื่องมือสร้างโค้ดสําหรับบล็อกดังกล่าวมีความซับซ้อนกว่าเล็กน้อย แต่ก็ยังคงง่ายกว่าการเขียนและดูแลรักษาบล็อกหลายรายการ

ใช้ตัวเปลี่ยน

หากมีชุดบล็อกที่แสดงโครงสร้างการเขียนโปรแกรมเดียวกันในลักษณะต่างๆ คุณอาจสร้างบล็อกเดียวที่ใช้ตัวเปลี่ยนรูปแบบได้ เช่น บล็อก controls_if ในตัวสามารถแสดงคำสั่ง if-then-else หลายรูปแบบ