نمط التذكرة Memento

من موسوعة حسوب

نمط التذكرة هو نمط تصميم سلوكي يسمح لك بحفظ واسترجاع الحالة السابقة لكائن ما دون كشف تفاصيل استخداماته أو تطبيقاته (implementations).

المشكلة

تخيل أنك تنشئ برنامج محرر نصي، ويستطيع محررك هذا أن ينسق النصوص ويدخل صورًا سطرية (inline images) وغير ذلك، إضافة إلى ميزة تحرير النصوص الافتراضية. ولنقل أنك قررت عند نقطة ما أن تسمح للمستخدمين بإلغاء أي عمليات أجريت على النص، وهي ميزة انتشرت كثيرًا في السنين الماضية إلى حد أن الناس صارت تتوقع من أي تطبيق أو برنامج أن تكون فيه تلك الخاصية.

ولتطبيق هذه الخاصية هنا فإنك تختار الطريق المباشر، وهو جعل البرنامج يسجل حالة جميع الكائنات ويحفظها في ذاكرة ما قبل تنفيذ أي عملية، ثم يجلب البرنامج آخر نسخة من تاريخ تلك اللقطات المسجلة ويستخدمها لاستعادة حالة جميع الكائنات، وذلك عندما يقرر المستخدم استعادة إجراء أو عملية ما.

ضع الصورة.

قبل تنفيذ أي عملية فإن البرنامج يحفظ لقطة من حالة الكائنات يمكن استخدامها لاحقًا لاستعادة الكائنات لحالتها السابقة.

ستظهر هنا مشكلة في كيفية أخذ تلك اللقطات التي ستُحفظ، فالبديهي أن تنسخ قيم جميع الحقول في كائن ما إلى الذاكرة، لكن هذا لن ينجح إلا إن كان الكائن نفسه سيسمح لك لهذا، لكن أغلب الكائنات الحقيقية لن تسمح لغيرها بمعرفة ما فيها بسهولة، فهي تريد إخفاء البيانات الهامة في حقول خاصة (private).

لكن لنفرض أن الكائنات التي لدينا ستتصرف مثل الهيبيز، إذ تفضل العلاقات المفتوحة وتبقي حالتها عامة، ستحل هذه الفرضية المشكلة التي ذكرناها وستسمح لك بأخذ لقطات من حالات الكائنات متى شئت، لكن بها مشاكل كبيرة، فقد تقرر في المستقبل أن تعيد هيكلة بعض فئات المحرر، أو تقرر إضافة أو حذف بعض الحقول، سيتطلب هذا منك سَلسَلة (chaining) الفئات المسؤولة عن نسخ حالة الكائنات التي ستتأثر بتعديلك.

ضع الصورة. كيف تنسخ حالة الكائن الخاصة (private)

ويجب أن تحتوي اللقطة (snapshot) الفعلية على النص الفعلي وإحداثيات المؤشر (cursor coordinates) وموضع التمرير الحالي (scroll position)، ... . وستحتاج أن تجمع هذه القيم وتضعها في حاوية ما من أجل إنشاء لقطة لحالة المحرر. والغالب أنك ستخزن كثيرًا من كائنات الحاوية تلك داخل قائمة ما تمثل سجل التغييرات (history)، ومن ثم ستكون هذه الحاويات كائنات لفئة واحدة ليس لها أي أساليب لكن في نفس الوقت فيها حقول كثيرة تعكس حالة المحرر.

ومن أجل السماح للكائنات الأخرى بالكتابة والقراءة من وإلى اللقطة التي أخذناها، فسنحتاج إلى جعل الحقول عامة، وسيكشف هذا كل حالات المحرر سواء كانت خاصة أم لا، وستصير الفئات الأخرى معتمدة على كل تغيير صغير يحدث داخل الحقول الخاصة والأساليب دون التأثير على الفئات الخارجية (outer classes).

تقودك هذه الطريقة إلى طريق مسدود، فإما أن تكشف كل التفاصيل الداخلية للفئات جاعلًا إياها هشة أو تقيد الوصول إلى حالتها جاعلًا من المستحيل إنتاج لقطات، لذا لا بد من أسلوب أفضل لإدخال زر الإرجاع في المحرر.

الحل