یک سوال رایج این است که چگونه تعریف یک بلوک موجود را تغییر دهیم. برای مثال، ممکن است بخواهید یک بررسی اتصال اضافه کنید یا یک فیلد را به ورودی مقدار تغییر دهید.
به طور کلی، امکان تغییر تعریف بلوک در محل وجود ندارد.
نحوه اصلاح یک تعریف موجود
اگر می خواهید تعریف یک نوع بلوک موجود را تغییر دهید:
- یک کپی از تعریف موجود، از جمله مولدهای کد بلوک تهیه کنید.
- کپی را تغییر دهید و به نوع خود یک نام جدید بدهید.
- تعریف جدید خود را به
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
را نشان دهد.