یک سوال رایج این است که چگونه تعریف یک بلوک موجود را تغییر دهیم. برای مثال، ممکن است بخواهید یک بررسی اتصال اضافه کنید یا یک فیلد را به ورودی مقدار تغییر دهید.
به طور کلی، امکان تغییر تعریف بلوک در محل وجود ندارد.
نحوه اصلاح یک تعریف موجود
اگر می خواهید تعریف یک نوع بلوک موجود را تغییر دهید:
- یک کپی از تعریف موجود، از جمله مولدهای کد بلوک تهیه کنید.
- کپی را تغییر دهید و به نوع خود یک نام جدید بدهید.
- تعریف جدید خود را به
Blockly.Blocksاضافه کنید.
چرا نمی توانم مستقیماً یک تعریف موجود را تغییر دهم؟
اگر کنجکاو هستید که چرا نمی توانید یک تعریف موجود را تغییر دهید، ادامه مطلب را بخوانید. ما برخی از احتمالات را در نظر خواهیم گرفت.
یک تعریف موجود را مستقیماً اصلاح کنید
دو راه برای تغییر مستقیم تعریف بلوک موجود وجود دارد: monkeypatching و forking کد. هر دو به شدت منع می شوند، زیرا شما در معرض خطر شکستن کدی هستید که بستگی به کد میمون وصله شده یا فورک شده دارد. هر دو تکنیک همچنین ادغام به روز رسانی ها و رفع اشکال را دشوار می کنند. برای کسب اطلاعات بیشتر، در مورد monkeypatching چیست؟ و فورک بلوکی .
زیر کلاس یک تعریف موجود
ممکن است برای شما پیش بیاید که تعریف موجود را به نحوی زیر طبقه بندی کنید. متأسفانه، این امکان پذیر نیست زیرا تعاریف بلوک کلاس نیستند - آنها میکس هستند. به عنوان مثال، هیچ راهی برای بازنویسی یک ویژگی رنگ وجود ندارد زیرا این تعریف دارای ویژگی رنگ نیست. در عوض، یک تابع init دارد که setColour فراخوانی میکند تا ویژگی رنگ را روی بلوک تنظیم کند. از آنجا که فراخوانی در داخل init است، هیچ راهی برای جایگزینی آن بدون جایگزین کردن کل تابع init وجود ندارد.
بازنویسی یک تابع در یک تعریف موجود
امکان بازنویسی یک تابع در یک تعریف موجود وجود دارد:
Blockly.Blocks['existing_block'].init = function() {/*new function*/};
این کار می کند، اما شما باید تابع موجود را کپی و اصلاح کنید -- شما نمی توانید به طور جادویی فقط یک خط را جایگزین کنید. چندین مشکل در این مورد وجود دارد:
- تفاوت چندانی با کپی و اصلاح کل تعریف ندارد.
- شما نمی توانید از آن برای تغییر عملکرد
initبلوک هایی که در JSON تعریف شده اند، مانند بلوک های داخلی Blockly استفاده کنید. این به این دلیل است که هیچ تابعinitبرای کپی وجود ندارد -- در زمان اجرا تولید می شود. - از شما می خواهد که بلوک خود را با استفاده از جاوا اسکریپت تعریف کنید، که ممکن است باعث ایجاد مشکل در محلی سازی شود .
نتایج init را بازنویسی کنید
یک راه برای "جایگزینی فقط یک خط" از یک تابع 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
اگر دو بلوک دارید که به طور قابل ملاحظه ای مشابه هستند، می توانید یک تعریف 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 خود را در یک شی در دسترس عمومی تعریف کنید و آن شی را در تابع init به initJson منتقل کنید. این امکان را برای دیگران فراهم می کند تا ویژگی های فردی را بازنویسی کنند. برای اطلاعات بیشتر، بازنویسی یک جفت کلید-مقدار در تعریف JSON را ببینید.
استفاده مجدد از توابع
بلوکها ممکن است تعدادی از توابع استاندارد را تعریف کنند، مانند کنترلکنندههای رویداد در سطح بلوک، راهنماییهای ابزار سفارشی، اعتبارسنجیهای فیلد، و توابع مورد استفاده توسط جهشدهندهها، و همچنین توابعی که رفتار سفارشی را ارائه میکنند، مانند تابعی که مقادیر فیلد را از دادههای خارجی تنظیم میکند، مانند موقعیت فعلی بازوی ربات.
ممکن است امکان استفاده مجدد از این توابع در سراسر بلوک ها وجود داشته باشد.
از یک فیلد کشویی استفاده کنید
اگر مجموعهای از بلوکها دارید که اساساً به جز یک عملگر یکسان هستند، ممکن است بتوانید یک بلوک را طراحی کنید که دارای یک فیلد کشویی برای اپراتور باشد. به عنوان مثال:
- بلوک
logic_operationداخلی از یک کشویی با عملگرهاandوorاستفاده میکند. - بلوک
math_arithmeticداخلی از یک کشویی با عملگرهای+،-،×،÷و^استفاده می کند.
نوشتن مولدهای کد برای چنین بلوکهایی معمولاً کمی پیچیدهتر است، اما همچنان آسانتر از نوشتن و نگهداری چندین بلوک است.
از جهش دهنده استفاده کنید
اگر مجموعهای از بلوکها دارید که تغییرات متفاوتی از یک ساختار برنامهنویسی را نشان میدهند، ممکن است بتوانید یک بلوک واحد ایجاد کنید که از یک جهشدهنده استفاده میکند. به عنوان مثال، بلوک داخلی controls_if می تواند چندین گونه از دستورات if-then-else را نشان دهد.