نحوه اعمال الگوی واکنش در خارج از JS: مطالعه موردی

با نگاه کردن از یک زاویه مایل، React یک مشکل عجیب را حل می کند: چگونه یک زبان نشانه گذاری استاتیک را پویا کنیم. با قضاوت بر اساس محبوبیت React، بسیاری از ما موافقیم که React در حل این مشکل به خوبی کار میکند و کد با استفاده از این ایدئولوژی رندر کامپوننت اغلب به ماژولهای قابل استفاده مجدد منجر میشود.
به عنوان یک توسعه دهنده وب سابق، من همیشه React را به هر چارچوب دیگری ترجیح می دهم. با این حال، الگوهای طراحی عمومی کاربرد بسیار گسترده تری نسبت به توسعه وب دارند.
مشکلی که با آن روبرو بودم
مدت زمان زیادی است که با نرم افزار شبیه سازی Deepmind کار می کنم که درlti-جودینامیک int با شرکتntact یا به اختصار MuJoCo. برای استدلال ما، خیلی مهم نیست که دقیقاً MuJoCo چیست. ما فقط بر روی تعریف صحنه آن تمرکز خواهیم کرد. با طراحی، محیط های شبیه سازی شده از فایل های XML کامپایل می شوند که چیدمان صحنه شبیه سازی شده و اشیاء آن را مشخص می کند.
فقط برای داشتن یک ایده، می توانید در زیر یک مثال مینیمال با دو مکعب، یک کره و یک صفحه زمین پیدا کنید.
model="simple_scene">
name="ground" type="plane" size="10 10 0.1"/>
name="box_one" pos="0 0 0.1">
/>
name="box_geom" type="box" size="0.1 0.1 0.1"/>
name="box_two" pos="0 0 0.5">
/>
name="box_geom" type="box" size="0.1 0.1 0.1"/>
name="small_sphere" pos="0 0.5 0.1">
name="sphere_geom" type="sphere" size="0.05"/>
این صحنه به خصوص خسته کننده است زیرا اتفاق زیادی نخواهد افتاد، اما موازی با HTML باید کاملاً آشکار باشد. مشکل زمانی به وجود می آید که کسی چیزی پویاتر با تعداد متغیری از اشیا را بخواهد. در مورد من، من مجبور بودم به طور پویا گیره های ربات و اشیاء تصادفی را در صحنه های مختلف مبادله کنم.
بنابراین، هدف من بازآفرینی اصول اولیه React برای مدیریت بهتر بود.
خیلی ساده تر از چیزی بود که قبلا تصور می کردم.
اصول React
در سطح بالایی، React یک چارچوب ساده با یک نحو سفارشی به نام JSX برای تعیین کامپوننت ها ارائه می کند. کامپوننت ها واحدهای مستقلی هستند که منطق و نشانه گذاری خود را حفظ می کنند. کامپوننت ها همچنین می توانند از اجزای دیگری تشکیل شده باشند، بنابراین یک سلسله مراتب ایجاد می شود.
این یک ساختار درختی ایجاد می کند که مدل واقعی شی سند (DOM) را در مرورگر تقلید می کند. برای تولید HTML نهایی، React از یک متد “render” در کامپوننت ریشه استفاده می کند که به صورت بازگشتی متد “render” را روی تمام اجزای تو در تو فرا می خواند.
البته، React مدرن بسیار پیچیده تر از این است، اما این اصول اولیه را پوشش می دهد و تمام چیزی است که ما نیاز داریم.
تقلید React در پایتون
MuJoCo به زبان C/C++ نوشته شده است و اتصالات بومی پایتون را برای نمونه سازی سریع فراهم می کند. از آنجایی که قصد نداشتم JSX را در پایتون بازسازی کنم، به سادگی رشته چند خطی را با استفاده از متغیرهایی انتخاب کردم.
در صحنه مثال MuJoCo در بالا، این به صورت زیر ترجمه می شود:
XML = """
"simple_scene">
{objects}
"""
خوشبختانه، پایتون با یک قرارداد قالب بندی رشته ای همراه است که به ما امکان می دهد از متغیرهای نامگذاری شده استفاده کنیم ({objects}
در این مثال) برای جایگزینی در آینده با استفاده از format(object="string for replacement")
فراخوانی متد روی رشته الگو.
این امیدوار کننده به نظر می رسد.
به طور مشابه، ما می توانیم یک قالب XML برای اشیاء ایجاد کنیم. در اینجا یک نمونه رشته الگو وجود دارد که امکان تنظیمات اندازه سفارشی را برای یک شی جعبه فراهم می کند:
XML = """
"{name}" pos="0 0 0.1">
"box_geom" type="box" size="{size}"/>
"""
در مرحله بعد، ما به یک رابط برای هر کامپوننت نیاز داریم تا آنها را وادار به اشتراک گذاری روش مورد نیاز برای رندر کردن هر جزء کنیم:
from abc import ABC, abstractmethod
class MjXML(ABC):
@abstractmethod
def to_xml(self) -> str:
pass
در مرحله نهایی مونتاژ XML، هر جزء را فراخوانی می کنیم to_xml
متد، که رشته حاصل را برای هر مؤلفه ای که در قالب استفاده می شود، خروجی می دهد.
سپس، کامپوننت مربوطه را برای شی مثال بالا پیاده سازی می کنیم:
XML = """
"{name}" pos="0 0 0.1">
"box_geom" type="box" size="{size}"/>
"""
class SimpleBox(MjXML):
def __init__(self, name: str, size: float):
self.name = name
self.size = size
def to_xml(self):
box_xml = XML.format(name=self.name, size=self.size)
return box_xml
در نهایت کامپوننت را برای صحنه پیاده سازی می کنیم:
XML = """
"simple_scene">
{objects}
"""
class SimpleScene(MjXML):
def __init__(self, object_list: List[MjXML]):
self.object_list = object_list
def to_xml(self):
obj_xml = " ".join([obj.to_xml() for obj in self.object_list])
full_xml = XML.format(objects=obj_xml)
return full_xml
اساسا، این در حال حاضر یک نمونه حداقل قابل اجرا است.
برای شبیه سازی واقعی، باید مدل و داده های MuJoCo را با استفاده از رشته XML ساخته شده خود بسازیم. از آنجایی که این مقاله در مورد MuJoCo نیست، من بخش شبیه سازی را از این نمونه کد حذف کرده ام.
پسوندهای احتمالی
در حالی که این مثال مفهوم اصلی را پوشش می دهد، مورد استفاده خاص من به ویژگی های اضافی مانند بارگذاری پویا دارایی ها، تصادفی سازی نام ها و منطق برخورد سفارشی نیاز دارد. اگر به شبیه سازی های فیزیکی پیچیده تر علاقه دارید، به من اطلاع دهید.
با این حال، این ویژگیهای اضافی ممکن است پیام اصلی را که میخواهم منتقل کنم بیش از حد پیچیده کند: اگر با نشانهگذاری ثابت کار میکنید که به رفتار پویا نیاز دارد، استفاده از مکانیزم رندر مبتنی بر مؤلفه مشابه React را در نظر بگیرید. می تواند توسعه را ساده کرده و انعطاف پذیری برنامه های شما را افزایش دهد.