یادگاری (Memento)
هـــدف
الگوی طراحی رفتاری Memento (ممنتو) به شما اجازه میدهد تا حالت قبلی یک شیء را ذخیره و بازیابی کنید بدون اینکه جزئیات پیادهسازی داخلی آن را آشکار کند.
مـسئـــلـه
تصور کنید که در حال ایجاد یک برنامه ویرایشگر متن هستید. علاوه بر ویرایش ساده متن، ویرایشگر شما میتواند متن را قالببندی کند، تصاویر درون خطی را درج کند و غیره.
در برخی مواقع، تصمیم گرفتید به کاربران اجازه دهید هر عملی را که روی متن انجام میدهند بازگرداند. این ویژگی در طول سالها بسیار رایج شده است، بهطوری که امروزه مردم انتظار دارند هر برنامهای آن را داشته باشد. برای پیادهسازی، رویکرد مستقیم را انتخاب کردید. قبل از انجام هر عملی، برنامه حالت تمام اشیاء را ثبت میکند و آن را در برخی ذخیرهسازیها ذخیره میکند. بعداً، زمانی که یک کاربر تصمیم میگیرد یک عمل را بازگرداند، برنامه آخرین عکس را از تاریخچه بازیابی میکند و از آن برای بازگرداندن حالت تمام اشیاء استفاده میکند.
قبل از اجرای یک عملیات، برنامه یک عکس از حالت اشیاء ذخیره میکند که بعداً میتوان از آن برای بازگرداندن اشیاء به حالت قبلی آنها استفاده کرد.
بیایید در مورد آن عکسهای حالت فکر کنیم. دقیقاً چگونه میتوانید یکی از آنها را تولید کنید؟ احتمالاً باید تمام فیلدها را در یک شیء بررسی کنید و مقادیر آنها را در ذخیرهسازی کپی کنید. با این حال، این فقط در صورتی کار میکند که شیء محدودیتهای دسترسی بسیار سادهای به محتویات خود داشته باشد. متأسفانه، اکثر اشیاء واقعی به دیگران اجازه نمیدهند به راحتی داخل آنها نگاه کنند و تمام دادههای مهم را در فیلدهای خصوصی پنهان میکنند.
در حال حاضر این مشکل را نادیده بگیرید و فرض کنید اشیاء ما مانند هیپیها رفتار میکنند: ترجیح میدهند روابط باز داشته باشند و حالت خود را عمومی نگه دارند. در حالی که این رویکرد مشکل فوری را حل میکند و به شما اجازه میدهد عکسهای حالتهای اشیاء را به دلخواه تولید کنید، هنوز هم برخی مسائل جدی دارد. در آینده، ممکن است تصمیم بگیرید برخی از کلاسهای ویرایشگر را بازسازی کنید یا برخی از فیلدها را اضافه یا حذف کنید. به نظر آسان میرسد، اما این همچنین نیاز به تغییر کلاسهایی دارد که مسئول کپی کردن حالت اشیاء آسیبدیده هستند.
چگونه یک نسخه از حالت خصوصی شیء را ایجاد کنیم؟
اما چیز دیگری هم هست. بیایید عکسهای واقعی حالت ویرایشگر را در نظر بگیریم. چه دادههایی را شامل میشود؟ حداقل باید شامل متن واقعی، مختصات مکاننما، موقعیت اسکرول فعلی و غیره باشد. برای ایجاد یک عکس، باید این مقادیر را جمعآوری کرده و آنها را در نوعی ظرف قرار دهید.
به احتمال زیاد، تعداد زیادی از این اشیاء ظرف را در داخل یک لیستی که تاریخچه را نشان میدهد ذخیره خواهید کرد. بنابراین ظروف احتمالاً به اشیاء یک کلاس تبدیل میشوند. این کلاس تقریباً هیچ متدی نخواهد داشت، اما فیلدهای زیادی دارد که حالت ویرایشگر را بازتاب میدهند. برای اجازه دادن به اشیاء دیگر برای نوشتن و خواندن دادهها به داخل و از یک عکس، احتمالاً باید فیلدهای آن را عمومی کنید. این امر تمام حالتهای ویرایشگر را آشکار میکند، چه خصوصی باشند چه نباشند. کلاسهای دیگر به هر تغییر کوچکی در کلاس عکس وابسته میشوند که در غیر این صورت در فیلدها و متدهای خصوصی اتفاق میافتد بدون تأثیر بر کلاسهای خارجی.
به نظر میرسد به بنبست رسیدهایم: یا تمام جزئیات داخلی کلاسها را آشکار میکنید و آنها را بسیار شکننده میکنید، یا دسترسی به حالت آنها را محدود میکنید و این کار تولید عکسها را غیرممکن میکند. آیا راه دیگری برای پیادهسازی “بازگشت” وجود دارد؟
راهــکــــار
تمام مشکلاتی که ما با آنها مواجه شدیم ناشی از کپسولهسازی شکسته است. برخی از اشیاء سعی میکنند کارهای بیشتری نسبت به آنچه باید انجام دهند انجام دهند. برای جمعآوری دادههای مورد نیاز برای انجام یک عمل، آنها به فضای خصوصی اشیاء دیگر تجاوز میکنند تا اینکه اجازه دهند این اشیاء عمل واقعی را انجام دهند.
الگوی Memento ایجاد عکسهای حالت را به صاحب واقعی آن حالت، شیء مبدا، واگذار میکند. بنابراین، به جای اینکه اشیاء دیگر سعی کنند حالت ویرایشگر را از “خارج” کپی کنند، خود کلاس ویرایشگر میتواند عکس را ایجاد کند زیرا دسترسی کامل به حالت خود را دارد.
الگو پیشنهاد میکند که نسخه کپی شده از حالت شیء را در یک شیء خاص به نام memento ذخیره کنید. محتویات memento برای هیچ شیء دیگری به جز شیای که آن را تولید کرده است قابل دسترسی نیست. اشیاء دیگر باید با استفاده از یک رابط محدود که ممکن است اجازه بازیابی متاداده عکس (زمان ایجاد، نام عملیات انجام شده و غیره) را بدهد، اما نه حالت اصلی شیء موجود در عکس، با mementoها ارتباط برقرار کنند.
مبدأ دسترسی کامل به memento دارد، در حالی که مراقب فقط میتواند به متاداده دسترسی داشته باشد.
چنین سیاست محدودکنندهای به شما امکان میدهد mementoها را در داخل اشیاء دیگر ذخیره کنید، که معمولاً مراقب نامیده میشوند. از آنجایی که مراقب فقط از طریق رابط محدود با memento کار میکند، نمیتواند با حالت ذخیره شده در داخل memento دستکاری کند. در عین حال، مبدا دسترسی به تمام فیلدها در داخل memento را دارد و این امکان را میدهد تا حالت قبلی خود را به دلخواه بازیابی کند.
در مثال ویرایشگر متن ما، میتوانیم یک کلاس تاریخچه جداگانه ایجاد کنیم تا به عنوان مراقب عمل کند. یک پشته از mementoهای ذخیره شده در داخل مراقب هر بار که ویرایشگر در حال اجرای یک عملیات است رشد میکند. حتی میتوانید این پشته را در داخل رابط کاربری برنامه رندر کنید و تاریخچه عملیاتهای قبلی را به کاربر نمایش دهید.
هنگامی که یک کاربر undo را فعال میکند، تاریخچه آخرین memento را از پشته میگیرد و آن را به ویرایشگر برمیگرداند و درخواست بازگشت را میکند. از آنجایی که ویرایشگر دسترسی کامل به memento دارد، حالت خود را با مقادیر گرفته شده از memento تغییر میدهد.
ســاخــتـــار
پیادهسازی مبتنی بر کلاسهای تو در تو
پیادهسازی کلاسیک الگو به پشتیبانی از کلاسهای تو در تو متکی است که در بسیاری از زبانهای برنامهنویسی محبوب (مانند C++، C# و جاوا) در دسترس است.
پیادهسازی مبتنی بر یک رابط واسط
یک پیادهسازی جایگزین وجود دارد که برای زبانهای برنامهنویسی که از کلاسهای تو در تو پشتیبانی نمیکنند مناسب است (بله، PHP، من در مورد شما صحبت میکنم).
پیادهسازی با کپسولهسازی حتی سختگیرانهتر
پیادهسازی دیگری وجود دارد که زمانی مفید است که حتی کوچکترین فرصتی برای دسترسی سایر کلاسها به حالت مبدا از طریق memento را نمیخواهید.
برای مطـالعـه متن کامل مقالــه، مجموعه کــــدها، نحوه پیاده سازی، مزایا و معایب و روابط با الگوهای دیگر، ایــنــجـــا کلیک کنید.