---
title: "التذكار"
type: "design-pattern"
slug: "memento"
url: "http://localhost:3000/ar/design-patterns/memento.md"
category: "الأنماط السلوكية"
description: "التذكار هو نمط تصميم سلوكي يتيح لك حفظ الحالة السابقة لكائن واستعادتها دون الكشف عن تفاصيل تنفيذه."
languages: ["java", "csharp", "cpp", "go", "php", "python", "ruby", "rust", "swift", "typescript"]
---
# التذكار

> التذكار هو نمط تصميم سلوكي يتيح لك حفظ الحالة السابقة لكائن واستعادتها دون الكشف عن تفاصيل تنفيذه.

## Intent

**التذكار** هو نمط تصميم سلوكي يتيح لك حفظ الحالة السابقة لكائن واستعادتها دون الكشف عن تفاصيل تنفيذه.

## Problem

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

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

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

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

لنتجاهل تلك المشكلة الآن ولنفترض أن كائناتنا تتصرّف كالهيبيز: تفضّل العلاقات المفتوحة وتُبقي حالتها عامة (public). ومع أن هذا النهج سيحلّ المشكلة الآنيّة ويتيح لك إنتاج لقطات من حالات الكائنات وقتما تشاء، إلا أنه لا يزال يعاني من بعض المشكلات الجدّية. ففي المستقبل قد تقرّر إعادة هيكلة بعض فئات المحرّر، أو إضافة بعض الحقول أو إزالتها. يبدو الأمر سهلًا، لكنه سيتطلّب أيضًا تعديل الفئات المسؤولة عن نسخ حالة الكائنات المتأثّرة.

كيف تُنشئ نسخة من الحالة الخاصة للكائن؟

لكن الأمر لا يتوقف عند هذا الحدّ. لنتأمّل «اللقطات» الفعلية لحالة المحرّر. ما البيانات التي تحتويها؟ كحدّ أدنى مطلق، يجب أن تحتوي على النص الفعلي، وإحداثيات المؤشّر، وموضع التمرير الحالي، وما إلى ذلك. ولإنشاء لقطة، ستحتاج إلى جمع هذه القيم ووضعها في نوع ما من الحاويات.

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

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

## Solution

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

يفوّض نمط التذكار مهمة إنشاء لقطات الحالة إلى المالك الفعلي لتلك الحالة، وهو كائن _المُنشئ_ (originator). وبذلك، بدلًا من أن تحاول كائنات أخرى نسخ حالة المحرّر من «الخارج»، تستطيع فئة المحرّر نفسها إنشاء اللقطة لأنها تملك وصولًا كاملًا إلى حالتها الخاصة.

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

يملك المُنشئ وصولًا كاملًا إلى التذكار، بينما لا يستطيع الوصيّ الوصول إلا إلى البيانات الوصفية.

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

في مثال محرّر النصوص لدينا، يمكننا إنشاء فئة سجلّ منفصلة لتؤدّي دور الوصيّ. وستنمو مكدّسة (stack) من التذكارات مخزّنة داخل الوصيّ في كل مرة يوشك فيها المحرّر على تنفيذ عملية. ويمكنك حتى عرض هذه المكدّسة ضمن واجهة استخدام التطبيق، لتُظهر للمستخدم سجلّ العمليات المُنفَّذة سابقًا.

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

## Structure

#### تنفيذ قائم على الفئات المتداخلة

يعتمد التنفيذ الكلاسيكي للنمط على دعم الفئات المتداخلة (nested classes)، المتوفّر في كثير من لغات البرمجة الشائعة (مثل C++ وC# وJava).

1. تستطيع فئة **المُنشئ (Originator)** إنتاج لقطات من حالتها الخاصة، إضافةً إلى استعادة حالتها من اللقطات عند الحاجة.
2. **التذكار (Memento)** هو كائن قيمة (value object) يعمل بمثابة لقطة من حالة المُنشئ. ومن الممارسات الشائعة جعل التذكار غير قابل للتغيير (immutable) وتمرير البيانات إليه مرة واحدة فقط عبر الباني (constructor).
3. يعرف **الوصيّ (Caretaker)** ليس فقط «متى» و«لماذا» يلتقط حالة المُنشئ، بل أيضًا متى ينبغي استعادة الحالة.
يمكن للوصيّ تتبّع تاريخ المُنشئ عبر تخزين مكدّسة من التذكارات. وعندما يحتاج المُنشئ إلى العودة إلى الوراء في التاريخ، يجلب الوصيّ التذكار الأعلى من المكدّسة ويمرّره إلى دالة الاستعادة في المُنشئ.
4. في هذا التنفيذ، تكون فئة التذكار متداخلة داخل المُنشئ. وهذا يتيح للمُنشئ الوصول إلى حقول التذكار ودوالّه، رغم أنها معلَنة على أنها خاصة (private). ومن جهة أخرى، يملك الوصيّ وصولًا محدودًا جدًا إلى حقول التذكار ودوالّه، مما يتيح له تخزين التذكارات في مكدّسة دون العبث بحالتها.

#### تنفيذ قائم على واجهة وسيطة

ثمة تنفيذ بديل يناسب لغات البرمجة التي لا تدعم الفئات المتداخلة (نعم يا PHP، أعنيكِ أنتِ).

1. في غياب الفئات المتداخلة، يمكنك تقييد الوصول إلى حقول التذكار عبر إرساء اتّفاق مفاده أن الأوصياء لا يمكنهم التعامل مع التذكار إلا من خلال واجهة وسيطة معلَنة صراحةً، لا تعلن إلا الدوالّ المتعلّقة بالبيانات الوصفية للتذكار.
2. ومن جهة أخرى، يستطيع المُنشئون التعامل مع كائن التذكار مباشرةً، مع الوصول إلى الحقول والدوالّ المعلَنة في فئة التذكار. وعيب هذا النهج هو أنك تحتاج إلى إعلان جميع أعضاء التذكار على أنها عامة (public).

#### تنفيذ بتغليف أكثر صرامة

ثمة تنفيذ آخر يكون مفيدًا عندما لا تريد ترك أدنى فرصة للفئات الأخرى للوصول إلى حالة المُنشئ عبر التذكار.

1. يتيح هذا التنفيذ وجود أنواع متعدّدة من المُنشئين والتذكارات. ويعمل كل مُنشئ مع فئة تذكار مقابِلة له. ولا يكشف المُنشئون ولا التذكارات حالتهم لأي أحد.
2. يُمنَع الأوصياء الآن صراحةً من تغيير الحالة المخزّنة في التذكارات. وعلاوةً على ذلك، تصبح فئة الوصيّ مستقلّة عن المُنشئ لأن دالة الاستعادة باتت معرَّفة الآن في فئة التذكار.
3. يصبح كل تذكار مرتبطًا بالمُنشئ الذي أنتجه. ويمرّر المُنشئ نفسه إلى باني التذكار، مع قيم حالته. وبفضل العلاقة الوثيقة بين هاتين الفئتين، يستطيع التذكار استعادة حالة مُنشئه، شريطة أن يكون الأخير قد عرّف دوالّ التعيين (setters) المناسبة.

## Pseudocode

يستخدم هذا المثال نمط التذكار جنبًا إلى جنب مع نمط [الأمر](/ar/design-patterns/command) لتخزين لقطات من حالة محرّر النصوص المعقّد واستعادة حالة سابقة من هذه اللقطات عند الحاجة.

حفظ لقطات من حالة محرّر النصوص.

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

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

// المُنشئ يحتفظ ببعض البيانات المهمّة التي قد تتغيّر بمرور
// الوقت. كما يعرّف دالةً لحفظ حالته داخل تذكار ودالةً أخرى
// لاستعادة الحالة منه.
class Editor is
    private field text, curX, curY, selectionWidth

    method setText(text) is
        this.text = text

    method setCursor(x, y) is
        this.curX = x
        this.curY = y

    method setSelectionWidth(width) is
        this.selectionWidth = width

    // يحفظ الحالة الحالية داخل تذكار.
    method createSnapshot():Snapshot is
        // التذكار كائن غير قابل للتغيير؛ ولهذا يمرّر المُنشئ
        // حالته إلى مُعامِلات باني التذكار.
        return new Snapshot(this, text, curX, curY, selectionWidth)

// فئة التذكار تخزّن الحالة السابقة للمحرّر.
class Snapshot is
    private field editor: Editor
    private field text, curX, curY, selectionWidth

    constructor Snapshot(editor, text, curX, curY, selectionWidth) is
        this.editor = editor
        this.text = text
        this.curX = x
        this.curY = y
        this.selectionWidth = selectionWidth

    // في مرحلة ما، يمكن استعادة حالة سابقة للمحرّر باستخدام
    // كائن تذكار.
    method restore() is
        editor.setText(text)
        editor.setCursor(curX, curY)
        editor.setSelectionWidth(selectionWidth)

// يمكن لكائن الأمر أن يعمل بوصفه وصيًّا. وفي تلك الحالة،
// يحصل الأمر على تذكار قبيل تغييره لحالة المُنشئ مباشرةً.
// وعند طلب التراجع، يستعيد حالة المُنشئ من تذكار.
class Command is
    private field backup: Snapshot

    method makeBackup() is
        backup = editor.createSnapshot()

    method undo() is
        if (backup != null)
            backup.restore()
    // ...

## Applicability

استخدم نمط التذكار عندما تريد إنتاج لقطات (snapshots) من حالة الكائن لتتمكّن من استعادة حالة سابقة له.

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

 استخدم النمط عندما يكون الوصول المباشر إلى حقول الكائن أو دوال الجلب/التعيين (getters/setters) مُخِلًّا بتغليفه (encapsulation).

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

## How to Implement

1. حدِّد الفئة التي ستؤدّي دور المُنشئ. من المهم معرفة ما إذا كان البرنامج يستخدم كائنًا مركزيًا واحدًا من هذا النوع أم عدّة كائنات أصغر.
2. أنشئ فئة التذكار. وأعلِن، واحدًا تلو الآخر، مجموعة من الحقول تعكس الحقول المعلَنة داخل فئة المُنشئ.
3. اجعل فئة التذكار غير قابلة للتغيير (immutable). فينبغي للتذكار أن يقبل البيانات مرة واحدة فقط عبر الباني. ولا ينبغي أن تمتلك الفئة أي دوالّ تعيين (setters).
4. إذا كانت لغة برمجتك تدعم الفئات المتداخلة، فضع التذكار متداخلًا داخل المُنشئ. وإن لم تكن كذلك، فاستخرج واجهة فارغة من فئة التذكار واجعل جميع الكائنات الأخرى تستخدمها للإشارة إلى التذكار. ويمكنك إضافة بعض عمليات البيانات الوصفية إلى الواجهة، لكن دون أي شيء يكشف حالة المُنشئ.
5. أضِف إلى فئة المُنشئ دالةً لإنتاج التذكارات. وينبغي للمُنشئ أن يمرّر حالته إلى التذكار عبر وسيط واحد أو أكثر من وسائط باني التذكار.
وينبغي أن يكون نوع القيمة المُعادة من الدالة هو الواجهة التي استخرجتها في الخطوة السابقة (بافتراض أنك استخرجتها أصلًا). وخلف الكواليس، ينبغي للدالة المنتِجة للتذكارات أن تتعامل مباشرةً مع فئة التذكار.
6. أضِف إلى فئة المُنشئ دالةً لاستعادة حالته. وينبغي أن تقبل كائن تذكار كوسيط. وإن كنت قد استخرجت واجهة في الخطوة السابقة، فاجعلها نوع المُعامِل. وفي هذه الحالة، ستحتاج إلى تحويل نوع (typecast) الكائن الوارد إلى فئة التذكار، لأن المُنشئ يحتاج إلى وصول كامل إلى ذلك الكائن.
7. ينبغي للوصيّ، سواء أكان يمثّل كائن أمر أم سجلًّا أم شيئًا مختلفًا تمامًا، أن يعرف متى يطلب تذكارات جديدة من المُنشئ، وكيف يخزّنها، ومتى يستعيد حالة المُنشئ بتذكار معيّن.
8. يمكن نقل الرابط بين الأوصياء والمُنشئين إلى داخل فئة التذكار. وفي هذه الحالة، يجب أن يكون كل تذكار متّصلًا بالمُنشئ الذي أنشأه. وستنتقل دالة الاستعادة أيضًا إلى فئة التذكار. غير أن هذا كلّه لن يكون منطقيًا إلا إذا كانت فئة التذكار متداخلة داخل المُنشئ أو كانت فئة المُنشئ توفّر دوالّ تعيين (setters) كافية لإعادة كتابة حالتها.

## Pros

* يمكنك إنتاج لقطات من حالة الكائن دون الإخلال بتغليفه.
* يمكنك تبسيط كود المُنشئ بترك مهمة الاحتفاظ بتاريخ حالة المُنشئ للوصيّ.

## Cons

* قد يستهلك التطبيق قدرًا كبيرًا من ذاكرة الوصول العشوائي (RAM) إذا أنشأ العملاء التذكارات بكثرة.
* ينبغي على الأوصياء (caretakers) تتبّع دورة حياة المُنشئ (originator) ليتمكّنوا من إتلاف التذكارات التي لم تعد مستخدمة.
* لا تستطيع معظم لغات البرمجة الديناميكية، مثل PHP وPython وJavaScript، ضمان بقاء الحالة الموجودة داخل التذكار دون مساس.

## Relations with Other Patterns

* يمكنك استخدام نمطَي [الأمر](/ar/design-patterns/command) و[التذكار](/ar/design-patterns/memento) معًا عند تنفيذ ميزة «التراجع». وفي هذه الحالة، تكون الأوامر مسؤولة عن تنفيذ عمليات مختلفة على كائن هدف، بينما تحفظ التذكارات حالة ذلك الكائن قبيل تنفيذ الأمر مباشرةً.
* يمكنك استخدام [التذكار](/ar/design-patterns/memento) مع [المُكرِّر](/ar/design-patterns/iterator) لالتقاط حالة التكرار الحالية والتراجع عنها عند الضرورة.
* أحيانًا يكون [النموذج الأولي](/ar/design-patterns/prototype) بديلًا أبسط من [التذكار](/ar/design-patterns/memento). وينجح ذلك إذا كان الكائن الذي تريد تخزين حالته في السجلّ بسيطًا إلى حدّ معقول ولا يملك روابط إلى موارد خارجية، أو كانت الروابط سهلة إعادة الإنشاء.
## Relations

**Related patterns**

- [الأمر (Command)](/ar/design-patterns/command.md)
- [المُكرِّر](/ar/design-patterns/iterator.md)
- [النموذج الأولي (Prototype)](/ar/design-patterns/prototype.md)

## Code Examples

### java

```java
package refactoring_guru.memento.example.editor;

import refactoring_guru.memento.example.commands.Command;
import refactoring_guru.memento.example.history.History;
import refactoring_guru.memento.example.history.Memento;
import refactoring_guru.memento.example.shapes.CompoundShape;
import refactoring_guru.memento.example.shapes.Shape;

import javax.swing.*;
import java.io.*;
import java.util.Base64;

public class Editor extends JComponent {
    private Canvas canvas;
    private CompoundShape allShapes = new CompoundShape();
    private History history;

    public Editor() {
        canvas = new Canvas(this);
        history = new History();
    }

    public void loadShapes(Shape... shapes) {
        allShapes.clear();
        allShapes.add(shapes);
        canvas.refresh();
    }

    public CompoundShape getShapes() {
        return allShapes;
    }

    public void execute(Command c) {
        history.push(c, new Memento(this));
        c.execute();
    }

    public void undo() {
        if (history.undo())
            canvas.repaint();
    }

    public void redo() {
        if (history.redo())
            canvas.repaint();
    }

    public String backup() {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this.allShapes);
            oos.close();
            return Base64.getEncoder().encodeToString(baos.toByteArray());
        } catch (IOException e) {
            return "";
        }
    }

    public void restore(String state) {
        try {
            byte[] data = Base64.getDecoder().decode(state);
            ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));
            this.allShapes = (CompoundShape) ois.readObject();
            ois.close();
        } catch (ClassNotFoundException e) {
            System.out.print("ClassNotFoundException occurred.");
        } catch (IOException e) {
            System.out.print("IOException occurred.");
        }
    }
}

package refactoring_guru.memento.example.editor;

import refactoring_guru.memento.example.commands.ColorCommand;
import refactoring_guru.memento.example.commands.MoveCommand;
import refactoring_guru.memento.example.shapes.Shape;

import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;

class Canvas extends java.awt.Canvas {
    private Editor editor;
    private JFrame frame;
    private static final int PADDING = 10;

    Canvas(Editor editor) {
        this.editor = editor;
        createFrame();
        attachKeyboardListeners();
        attachMouseListeners();
        refresh();
    }

    private void createFrame() {
        frame = new JFrame();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);

        JPanel contentPanel = new JPanel();
        Border padding = BorderFactory.createEmptyBorder(PADDING, PADDING, PADDING, PADDING);
        contentPanel.setBorder(padding);
        contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS));
        frame.setContentPane(contentPanel);

        contentPanel.add(new JLabel("Select and drag to move."), BorderLayout.PAGE_END);
        contentPanel.add(new JLabel("Right click to change color."), BorderLayout.PAGE_END);
        contentPanel.add(new JLabel("Undo: Ctrl+Z, Redo: Ctrl+R"), BorderLayout.PAGE_END);
        contentPanel.add(this);
        frame.setVisible(true);
        contentPanel.setBackground(Color.LIGHT_GRAY);
    }

    private void attachKeyboardListeners() {
        addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if ((e.getModifiers() & KeyEvent.CTRL_MASK) != 0) {
                    switch (e.getKeyCode()) {
                        case KeyEvent.VK_Z:
                            editor.undo();
                            break;
                        case KeyEvent.VK_R:
                            editor.redo();
                            break;
                    }
                }
            }
        });
    }

    private void attachMouseListeners() {
        MouseAdapter colorizer = new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                if (e.getButton() != MouseEvent.BUTTON3) {
                    return;
                }
                Shape target = editor.getShapes().getChildAt(e.getX(), e.getY());
                if (target != null) {
                    editor.execute(new ColorCommand(editor, new Color((int) (Math.random() * 0x1000000))));
                    repaint();
                }
            }
        };
        addMouseListener(colorizer);

        MouseAdapter selector = new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                if (e.getButton() != MouseEvent.BUTTON1) {
                    return;
                }

                Shape target = editor.getShapes().getChildAt(e.getX(), e.getY());
                boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) == ActionEvent.CTRL_MASK;

                if (target == null) {
                    if (!ctrl) {
                        editor.getShapes().unSelect();
                    }
                } else {
                    if (ctrl) {
                        if (target.isSelected()) {
                            target.unSelect();
                        } else {
                            target.select();
                        }
                    } else {
                        if (!target.isSelected()) {
                            editor.getShapes().unSelect();
                        }
                        target.select();
                    }
                }
                repaint();
            }
        };
        addMouseListener(selector);


        MouseAdapter dragger = new MouseAdapter() {
            MoveCommand moveCommand;

            @Override
            public void mouseDragged(MouseEvent e) {
                if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) != MouseEvent.BUTTON1_DOWN_MASK) {
                    return;
                }
                if (moveCommand == null) {
                    moveCommand = new MoveCommand(editor);
                    moveCommand.start(e.getX(), e.getY());
                }
                moveCommand.move(e.getX(), e.getY());
                repaint();
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                if (e.getButton() != MouseEvent.BUTTON1 || moveCommand == null) {
                    return;
                }
                moveCommand.stop(e.getX(), e.getY());
                editor.execute(moveCommand);
                this.moveCommand = null;
                repaint();
            }
        };
        addMouseListener(dragger);
        addMouseMotionListener(dragger);
    }

    public int getWidth() {
        return editor.getShapes().getX() + editor.getShapes().getWidth() + PADDING;
    }

    public int getHeight() {
        return editor.getShapes().getY() + editor.getShapes().getHeight() + PADDING;
    }

    void refresh() {
        this.setSize(getWidth(), getHeight());
        frame.pack();
    }

    public void update(Graphics g) {
        paint(g);
    }

    public void paint(Graphics graphics) {
        BufferedImage buffer = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
        Graphics2D ig2 = buffer.createGraphics();
        ig2.setBackground(Color.WHITE);
        ig2.clearRect(0, 0, this.getWidth(), this.getHeight());

        editor.getShapes().paint(buffer.getGraphics());

        graphics.drawImage(buffer, 0, 0, null);
    }
}

package refactoring_guru.memento.example.history;

import refactoring_guru.memento.example.commands.Command;

import java.util.ArrayList;
import java.util.List;

public class History {
    private List<Pair> history = new ArrayList<Pair>();
    private int virtualSize = 0;

    private class Pair {
        Command command;
        Memento memento;
        Pair(Command c, Memento m) {
            command = c;
            memento = m;
        }

        private Command getCommand() {
            return command;
        }

        private Memento getMemento() {
            return memento;
        }
    }

    public void push(Command c, Memento m) {
        if (virtualSize != history.size() && virtualSize > 0) {
            history = history.subList(0, virtualSize - 1);
        }
        history.add(new Pair(c, m));
        virtualSize = history.size();
    }

    public boolean undo() {
        Pair pair = getUndo();
        if (pair == null) {
            return false;
        }
        System.out.println("Undoing: " + pair.getCommand().getName());
        pair.getMemento().restore();
        return true;
    }

    public boolean redo() {
        Pair pair = getRedo();
        if (pair == null) {
            return false;
        }
        System.out.println("Redoing: " + pair.getCommand().getName());
        pair.getMemento().restore();
        pair.getCommand().execute();
        return true;
    }

    private Pair getUndo() {
        if (virtualSize == 0) {
            return null;
        }
        virtualSize = Math.max(0, virtualSize - 1);
        return history.get(virtualSize);
    }

    private Pair getRedo() {
        if (virtualSize == history.size()) {
            return null;
        }
        virtualSize = Math.min(history.size(), virtualSize + 1);
        return history.get(virtualSize - 1);
    }
}

package refactoring_guru.memento.example.history;

import refactoring_guru.memento.example.editor.Editor;

public class Memento {
    private String backup;
    private Editor editor;

    public Memento(Editor editor) {
        this.editor = editor;
        this.backup = editor.backup();
    }

    public void restore() {
        editor.restore(backup);
    }
}

package refactoring_guru.memento.example.commands;

public interface Command {
    String getName();
    void execute();
}

package refactoring_guru.memento.example.commands;

import refactoring_guru.memento.example.editor.Editor;
import refactoring_guru.memento.example.shapes.Shape;

import java.awt.*;

public class ColorCommand implements Command {
    private Editor editor;
    private Color color;

    public ColorCommand(Editor editor, Color color) {
        this.editor = editor;
        this.color = color;
    }

    @Override
    public String getName() {
        return "Colorize: " + color.toString();
    }

    @Override
    public void execute() {
        for (Shape child : editor.getShapes().getSelected()) {
            child.setColor(color);
        }
    }
}

package refactoring_guru.memento.example.commands;

import refactoring_guru.memento.example.editor.Editor;
import refactoring_guru.memento.example.shapes.Shape;

public class MoveCommand implements Command {
    private Editor editor;
    private int startX, startY;
    private int endX, endY;

    public MoveCommand(Editor editor) {
        this.editor = editor;
    }

    @Override
    public String getName() {
        return "Move by X:" + (endX - startX) + " Y:" + (endY - startY);
    }

    public void start(int x, int y) {
        startX = x;
        startY = y;
        for (Shape child : editor.getShapes().getSelected()) {
            child.drag();
        }
    }

    public void move(int x, int y) {
        for (Shape child : editor.getShapes().getSelected()) {
            child.moveTo(x - startX, y - startY);
        }
    }

    public void stop(int x, int y) {
        endX = x;
        endY = y;
        for (Shape child : editor.getShapes().getSelected()) {
            child.drop();
        }
    }

    @Override
    public void execute() {
        for (Shape child : editor.getShapes().getSelected()) {
            child.moveBy(endX - startX, endY - startY);
        }
    }
}

package refactoring_guru.memento.example.shapes;

import java.awt.*;
import java.io.Serializable;

public interface Shape extends Serializable {
    int getX();
    int getY();
    int getWidth();
    int getHeight();
    void drag();
    void drop();
    void moveTo(int x, int y);
    void moveBy(int x, int y);
    boolean isInsideBounds(int x, int y);
    Color getColor();
    void setColor(Color color);
    void select();
    void unSelect();
    boolean isSelected();
    void paint(Graphics graphics);
}

package refactoring_guru.memento.example.shapes;

import java.awt.*;

public abstract class BaseShape implements Shape {
    int x, y;
    private int dx = 0, dy = 0;
    private Color color;
    private boolean selected = false;

    BaseShape(int x, int y, Color color) {
        this.x = x;
        this.y = y;
        this.color = color;
    }

    @Override
    public int getX() {
        return x;
    }

    @Override
    public int getY() {
        return y;
    }

    @Override
    public int getWidth() {
        return 0;
    }

    @Override
    public int getHeight() {
        return 0;
    }

    @Override
    public void drag() {
        dx = x;
        dy = y;
    }

    @Override
    public void moveTo(int x, int y) {
        this.x = dx + x;
        this.y = dy + y;
    }

    @Override
    public void moveBy(int x, int y) {
        this.x += x;
        this.y += y;
    }

    @Override
    public void drop() {
        this.x = dx;
        this.y = dy;
    }

    @Override
    public boolean isInsideBounds(int x, int y) {
        return x > getX() && x < (getX() + getWidth()) &&
                y > getY() && y < (getY() + getHeight());
    }

    @Override
    public Color getColor() {
        return color;
    }

    @Override
    public void setColor(Color color) {
        this.color = color;
    }

    @Override
    public void select() {
        selected = true;
    }

    @Override
    public void unSelect() {
        selected = false;
    }

    @Override
    public boolean isSelected() {
        return selected;
    }

    void enableSelectionStyle(Graphics graphics) {
        graphics.setColor(Color.LIGHT_GRAY);

        Graphics2D g2 = (Graphics2D) graphics;
        float[] dash1 = {2.0f};
        g2.setStroke(new BasicStroke(1.0f,
                BasicStroke.CAP_BUTT,
                BasicStroke.JOIN_MITER,
                2.0f, dash1, 0.0f));
    }

    void disableSelectionStyle(Graphics graphics) {
        graphics.setColor(color);
        Graphics2D g2 = (Graphics2D) graphics;
        g2.setStroke(new BasicStroke());
    }

    @Override
    public void paint(Graphics graphics) {
        if (isSelected()) {
            enableSelectionStyle(graphics);
        }
        else {
            disableSelectionStyle(graphics);
        }

        // ...
    }
}

package refactoring_guru.memento.example.shapes;

import java.awt.*;

public class Circle extends BaseShape {
    private int radius;

    public Circle(int x, int y, int radius, Color color) {
        super(x, y, color);
        this.radius = radius;
    }

    @Override
    public int getWidth() {
        return radius * 2;
    }

    @Override
    public int getHeight() {
        return radius * 2;
    }

    @Override
    public void paint(Graphics graphics) {
        super.paint(graphics);
        graphics.drawOval(x, y, getWidth() - 1, getHeight() - 1);
    }
}

package refactoring_guru.memento.example.shapes;

import java.awt.*;

public class Dot extends BaseShape {
    private final int DOT_SIZE = 3;

    public Dot(int x, int y, Color color) {
        super(x, y, color);
    }

    @Override
    public int getWidth() {
        return DOT_SIZE;
    }

    @Override
    public int getHeight() {
        return DOT_SIZE;
    }

    @Override
    public void paint(Graphics graphics) {
        super.paint(graphics);
        graphics.fillRect(x - 1, y - 1, getWidth(), getHeight());
    }
}

package refactoring_guru.memento.example.shapes;

import java.awt.*;

public class Rectangle extends BaseShape {
    private int width;
    private int height;

    public Rectangle(int x, int y, int width, int height, Color color) {
        super(x, y, color);
        this.width = width;
        this.height = height;
    }

    @Override
    public int getWidth() {
        return width;
    }

    @Override
    public int getHeight() {
        return height;
    }

    @Override
    public void paint(Graphics graphics) {
        super.paint(graphics);
        graphics.drawRect(x, y, getWidth() - 1, getHeight() - 1);
    }
}

package refactoring_guru.memento.example.shapes;

import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class CompoundShape extends BaseShape {
    private List<Shape> children = new ArrayList<>();

    public CompoundShape(Shape... components) {
        super(0, 0, Color.BLACK);
        add(components);
    }

    public void add(Shape component) {
        children.add(component);
    }

    public void add(Shape... components) {
        children.addAll(Arrays.asList(components));
    }

    public void remove(Shape child) {
        children.remove(child);
    }

    public void remove(Shape... components) {
        children.removeAll(Arrays.asList(components));
    }

    public void clear() {
        children.clear();
    }

    @Override
    public int getX() {
        if (children.size() == 0) {
            return 0;
        }
        int x = children.get(0).getX();
        for (Shape child : children) {
            if (child.getX() < x) {
                x = child.getX();
            }
        }
        return x;
    }

    @Override
    public int getY() {
        if (children.size() == 0) {
            return 0;
        }
        int y = children.get(0).getY();
        for (Shape child : children) {
            if (child.getY() < y) {
                y = child.getY();
            }
        }
        return y;
    }

    @Override
    public int getWidth() {
        int maxWidth = 0;
        int x = getX();
        for (Shape child : children) {
            int childsRelativeX = child.getX() - x;
            int childWidth = childsRelativeX + child.getWidth();
            if (childWidth > maxWidth) {
                maxWidth = childWidth;
            }
        }
        return maxWidth;
    }

    @Override
    public int getHeight() {
        int maxHeight = 0;
        int y = getY();
        for (Shape child : children) {
            int childsRelativeY = child.getY() - y;
            int childHeight = childsRelativeY + child.getHeight();
            if (childHeight > maxHeight) {
                maxHeight = childHeight;
            }
        }
        return maxHeight;
    }

    @Override
    public void drag() {
        for (Shape child : children) {
            child.drag();
        }
    }

    @Override
    public void drop() {
        for (Shape child : children) {
            child.drop();
        }
    }

    @Override
    public void moveTo(int x, int y) {
        for (Shape child : children) {
            child.moveTo(x, y);
        }
    }

    @Override
    public void moveBy(int x, int y) {
        for (Shape child : children) {
            child.moveBy(x, y);
        }
    }

    @Override
    public boolean isInsideBounds(int x, int y) {
        for (Shape child : children) {
            if (child.isInsideBounds(x, y)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void setColor(Color color) {
        super.setColor(color);
        for (Shape child : children) {
            child.setColor(color);
        }
    }

    @Override
    public void unSelect() {
        super.unSelect();
        for (Shape child : children) {
            child.unSelect();
        }
    }

    public Shape getChildAt(int x, int y) {
        for (Shape child : children) {
            if (child.isInsideBounds(x, y)) {
                return child;
            }
        }
        return null;
    }

    public boolean selectChildAt(int x, int y) {
        Shape child = getChildAt(x,y);
        if (child != null) {
            child.select();
            return true;
        }
        return false;
    }

    public List<Shape> getSelected() {
        List<Shape> selected = new ArrayList<>();
        for (Shape child : children) {
            if (child.isSelected()) {
                selected.add(child);
            }
        }
        return selected;
    }

    @Override
    public void paint(Graphics graphics) {
        if (isSelected()) {
            enableSelectionStyle(graphics);
            graphics.drawRect(getX() - 1, getY() - 1, getWidth() + 1, getHeight() + 1);
            disableSelectionStyle(graphics);
        }

        for (Shape child : children) {
            child.paint(graphics);
        }
    }
}

package refactoring_guru.memento.example;

import refactoring_guru.memento.example.editor.Editor;
import refactoring_guru.memento.example.shapes.Circle;
import refactoring_guru.memento.example.shapes.CompoundShape;
import refactoring_guru.memento.example.shapes.Dot;
import refactoring_guru.memento.example.shapes.Rectangle;

import java.awt.*;

public class Demo {
    public static void main(String[] args) {
        Editor editor = new Editor();
        editor.loadShapes(
                new Circle(10, 10, 10, Color.BLUE),

                new CompoundShape(
                        new Circle(110, 110, 50, Color.RED),
                        new Dot(160, 160, Color.RED)
                ),

                new CompoundShape(
                        new Rectangle(250, 250, 100, 100, Color.GREEN),
                        new Dot(240, 240, Color.GREEN),
                        new Dot(240, 360, Color.GREEN),
                        new Dot(360, 360, Color.GREEN),
                        new Dot(360, 240, Color.GREEN)
                )
        );
    }
}
```

### csharp

```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace RefactoringGuru.DesignPatterns.Memento.Conceptual
{
    // يحتفظ المُنشئ ببعض الحالة المهمّة التي قد تتغيّر بمرور الوقت. كما يعرّف دالةً لحفظ
    // الحالة داخل تذكار ودالةً أخرى لاستعادة الحالة منه.
    class Originator
    {
        // من أجل التبسيط، تُخزَّن حالة المُنشئ داخل متغيّر واحد.
        private string _state;

        public Originator(string state)
        {
            this._state = state;
            Console.WriteLine("Originator: My initial state is: " + state);
        }

        // قد يؤثّر منطق الأعمال الخاص بالمُنشئ في حالته الداخلية. ولذلك ينبغي على العميل
        // أخذ نسخة احتياطية من الحالة قبل استدعاء دوالّ منطق الأعمال عبر الدالة save().
        public void DoSomething()
        {
            Console.WriteLine("Originator: I'm doing something important.");
            this._state = this.GenerateRandomString(30);
            Console.WriteLine($"Originator: and my state has changed to: {_state}");
        }

        private string GenerateRandomString(int length = 10)
        {
            string allowedSymbols = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
            string result = string.Empty;

            while (length > 0)
            {
                result += allowedSymbols[new Random().Next(0, allowedSymbols.Length)];

                Thread.Sleep(12);

                length--;
            }

            return result;
        }

        // يحفظ الحالة الحالية داخل تذكار.
        public IMemento Save()
        {
            return new ConcreteMemento(this._state);
        }

        // يستعيد حالة المُنشئ من كائن تذكار.
        public void Restore(IMemento memento)
        {
            if (!(memento is ConcreteMemento))
            {
                throw new Exception("Unknown memento class " + memento.ToString());
            }

            this._state = memento.GetState();
            Console.Write($"Originator: My state has changed to: {_state}");
        }
    }

    // توفّر واجهة التذكار طريقةً لاسترجاع البيانات الوصفية للتذكار، مثل تاريخ الإنشاء أو
    // الاسم. غير أنها لا تكشف حالة المُنشئ.
    public interface IMemento
    {
        string GetName();

        string GetState();

        DateTime GetDate();
    }

    // يحتوي التذكار المحسوس (Concrete Memento) على البنية اللازمة لتخزين حالة المُنشئ.
    class ConcreteMemento : IMemento
    {
        private string _state;

        private DateTime _date;

        public ConcreteMemento(string state)
        {
            this._state = state;
            this._date = DateTime.Now;
        }

        // يستخدم المُنشئ هذه الدالة عند استعادة حالته.
        public string GetState()
        {
            return this._state;
        }
        
        // تُستخدم بقية الدوالّ من قِبَل الوصيّ لعرض البيانات الوصفية.
        public string GetName()
        {
            return $"{this._date} / ({this._state.Substring(0, 9)})...";
        }

        public DateTime GetDate()
        {
            return this._date;
        }
    }

    // لا يعتمد الوصيّ على فئة التذكار المحسوس. ولذلك لا يملك وصولًا إلى حالة المُنشئ
    // المخزّنة داخل التذكار. وهو يتعامل مع جميع التذكارات عبر واجهة التذكار الأساسية.
    class Caretaker
    {
        private List<IMemento> _mementos = new List<IMemento>();

        private Originator _originator = null;

        public Caretaker(Originator originator)
        {
            this._originator = originator;
        }

        public void Backup()
        {
            Console.WriteLine("\nCaretaker: Saving Originator's state...");
            this._mementos.Add(this._originator.Save());
        }

        public void Undo()
        {
            if (this._mementos.Count == 0)
            {
                return;
            }

            var memento = this._mementos.Last();
            this._mementos.Remove(memento);

            Console.WriteLine("Caretaker: Restoring state to: " + memento.GetName());

            try
            {
                this._originator.Restore(memento);
            }
            catch (Exception)
            {
                this.Undo();
            }
        }

        public void ShowHistory()
        {
            Console.WriteLine("Caretaker: Here's the list of mementos:");

            foreach (var memento in this._mementos)
            {
                Console.WriteLine(memento.GetName());
            }
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            // كود العميل.
            Originator originator = new Originator("Super-duper-super-puper-super.");
            Caretaker caretaker = new Caretaker(originator);

            caretaker.Backup();
            originator.DoSomething();

            caretaker.Backup();
            originator.DoSomething();

            caretaker.Backup();
            originator.DoSomething();

            Console.WriteLine();
            caretaker.ShowHistory();

            Console.WriteLine("\nClient: Now, let's rollback!\n");
            caretaker.Undo();

            Console.WriteLine("\n\nClient: Once more!\n");
            caretaker.Undo();

            Console.WriteLine();
        }
    }
}

Originator: My initial state is: Super-duper-super-puper-super.

Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: oGyQIIatlDDWNgYYqJATTmdwnnGZQj

Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: jBtMDDWogzzRJbTTmEwOOhZrjjBULe

Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: exoHyyRkbuuNEXOhhArKccUmexPPHZ

Caretaker: Here's the list of mementos:
12.06.2018 15:52:45 / (Super-dup...)
12.06.2018 15:52:46 / (oGyQIIatl...)
12.06.2018 15:52:46 / (jBtMDDWog...)

Client: Now, let's rollback!

Caretaker: Restoring state to: 12.06.2018 15:52:46 / (jBtMDDWog...)
Originator: My state has changed to: jBtMDDWogzzRJbTTmEwOOhZrjjBULe

Client: Once more!

Caretaker: Restoring state to: 12.06.2018 15:52:46 / (oGyQIIatl...)
Originator: My state has changed to: oGyQIIatlDDWNgYYqJATTmdwnnGZQj
```

### cpp

```cpp
/**
 * توفّر واجهة التذكار طريقةً لاسترجاع البيانات الوصفية للتذكار، مثل تاريخ الإنشاء أو
 * الاسم. غير أنها لا تكشف حالة المُنشئ.
 */
class Memento {
 public:
  virtual ~Memento() {}
  virtual std::string GetName() const = 0;
  virtual std::string date() const = 0;
  virtual std::string state() const = 0;
};

/**
 * يحتوي التذكار المحسوس (Concrete Memento) على البنية اللازمة لتخزين حالة المُنشئ.
 */
class ConcreteMemento : public Memento {
 private:
  std::string state_;
  std::string date_;

 public:
  ConcreteMemento(std::string state) : state_(state) {
    this->state_ = state;
    std::time_t now = std::time(0);
    this->date_ = std::ctime(&now);
  }
  /**
   * يستخدم المُنشئ هذه الدالة عند استعادة حالته.
   */
  std::string state() const override {
    return this->state_;
  }
  /**
   * تُستخدم بقية الدوالّ من قِبَل الوصيّ لعرض البيانات الوصفية.
   */
  std::string GetName() const override {
    return this->date_ + " / (" + this->state_.substr(0, 9) + "...)";
  }
  std::string date() const override {
    return this->date_;
  }
};

/**
 * يحتفظ المُنشئ ببعض الحالة المهمّة التي قد تتغيّر بمرور الوقت. كما يعرّف دالةً لحفظ
 * الحالة داخل تذكار ودالةً أخرى لاستعادة الحالة منه.
 */
class Originator {
  /**
   * @var string من أجل التبسيط، تُخزَّن حالة المُنشئ داخل متغيّر واحد.
   */
 private:
  std::string state_;

  std::string GenerateRandomString(int length = 10) {
    const char alphanum[] =
        "0123456789"
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "abcdefghijklmnopqrstuvwxyz";
    int stringLength = sizeof(alphanum) - 1;

    std::string random_string;
    for (int i = 0; i < length; i++) {
      random_string += alphanum[std::rand() % stringLength];
    }
    return random_string;
  }

 public:
  Originator(std::string state) : state_(state) {
    std::cout << "Originator: My initial state is: " << this->state_ << "\n";
  }
  /**
   * قد يؤثّر منطق الأعمال الخاص بالمُنشئ في حالته الداخلية. ولذلك ينبغي على العميل أخذ
   * نسخة احتياطية من الحالة قبل استدعاء دوالّ منطق الأعمال عبر الدالة save().
   */
  void DoSomething() {
    std::cout << "Originator: I'm doing something important.\n";
    this->state_ = this->GenerateRandomString(30);
    std::cout << "Originator: and my state has changed to: " << this->state_ << "\n";
  }

  /**
   * يحفظ الحالة الحالية داخل تذكار.
   */
  Memento *Save() {
    return new ConcreteMemento(this->state_);
  }
  /**
   * يستعيد حالة المُنشئ من كائن تذكار.
   */
  void Restore(Memento *memento) {
    this->state_ = memento->state();
    std::cout << "Originator: My state has changed to: " << this->state_ << "\n";
    delete memento;
  }
};

/**
 * لا يعتمد الوصيّ على فئة التذكار المحسوس. ولذلك لا يملك وصولًا إلى حالة المُنشئ المخزّنة
 * داخل التذكار. وهو يتعامل مع جميع التذكارات عبر واجهة التذكار الأساسية.
 */
class Caretaker {
  /**
   * @var Memento[]
   */
 private:
  std::vector<Memento *> mementos_;

  /**
   * @var Originator
   */
  Originator *originator_;

 public:
     Caretaker(Originator* originator) : originator_(originator) {
     }

     ~Caretaker() {
         for (auto m : mementos_) delete m;
     }

  void Backup() {
    std::cout << "\nCaretaker: Saving Originator's state...\n";
    this->mementos_.push_back(this->originator_->Save());
  }
  void Undo() {
    if (!this->mementos_.size()) {
      return;
    }
    Memento *memento = this->mementos_.back();
    this->mementos_.pop_back();
    std::cout << "Caretaker: Restoring state to: " << memento->GetName() << "\n";
    try {
      this->originator_->Restore(memento);
    } catch (...) {
      this->Undo();
    }
  }
  void ShowHistory() const {
    std::cout << "Caretaker: Here's the list of mementos:\n";
    for (Memento *memento : this->mementos_) {
      std::cout << memento->GetName() << "\n";
    }
  }
};
/**
 * كود العميل.
 */

void ClientCode() {
  Originator *originator = new Originator("Super-duper-super-puper-super.");
  Caretaker *caretaker = new Caretaker(originator);
  caretaker->Backup();
  originator->DoSomething();
  caretaker->Backup();
  originator->DoSomething();
  caretaker->Backup();
  originator->DoSomething();
  std::cout << "\n";
  caretaker->ShowHistory();
  std::cout << "\nClient: Now, let's rollback!\n\n";
  caretaker->Undo();
  std::cout << "\nClient: Once more!\n\n";
  caretaker->Undo();

  delete originator;
  delete caretaker;
}

int main() {
  std::srand(static_cast<unsigned int>(std::time(NULL)));
  ClientCode();
  return 0;
}

Originator: My initial state is: Super-duper-super-puper-super.

Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: uOInE8wmckHYPwZS7PtUTwuwZfCIbz

Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: te6RGmykRpbqaWo5MEwjji1fpM1t5D

Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: hX5xWDVljcQ9ydD7StUfbBt5Z7pcSN

Caretaker: Here's the list of mementos:
Sat Oct 19 18:09:37 2019
 / (Super-dup...)
Sat Oct 19 18:09:37 2019
 / (uOInE8wmc...)
Sat Oct 19 18:09:37 2019
 / (te6RGmykR...)

Client: Now, let's rollback!

Caretaker: Restoring state to: Sat Oct 19 18:09:37 2019
 / (te6RGmykR...)
Originator: My state has changed to: te6RGmykRpbqaWo5MEwjji1fpM1t5D

Client: Once more!

Caretaker: Restoring state to: Sat Oct 19 18:09:37 2019
 / (uOInE8wmc...)
Originator: My state has changed to: uOInE8wmckHYPwZS7PtUTwuwZfCIbz
```

### go

```go
package main

type Originator struct {
	state string
}

func (e *Originator) createMemento() *Memento {
	return &Memento{state: e.state}
}

func (e *Originator) restoreMemento(m *Memento) {
	e.state = m.getSavedState()
}

func (e *Originator) setState(state string) {
	e.state = state
}

func (e *Originator) getState() string {
	return e.state
}

package main

type Memento struct {
	state string
}

func (m *Memento) getSavedState() string {
	return m.state
}

package main

type Caretaker struct {
	mementoArray []*Memento
}

func (c *Caretaker) addMemento(m *Memento) {
	c.mementoArray = append(c.mementoArray, m)
}

func (c *Caretaker) getMemento(index int) *Memento {
	return c.mementoArray[index]
}

package main

import "fmt"

func main() {

	caretaker := &Caretaker{
		mementoArray: make([]*Memento, 0),
	}

	originator := &Originator{
		state: "A",
	}

	fmt.Printf("Originator Current State: %s\n", originator.getState())
	caretaker.addMemento(originator.createMemento())

	originator.setState("B")
	fmt.Printf("Originator Current State: %s\n", originator.getState())
	caretaker.addMemento(originator.createMemento())

	originator.setState("C")
	fmt.Printf("Originator Current State: %s\n", originator.getState())
	caretaker.addMemento(originator.createMemento())

	originator.restoreMemento(caretaker.getMemento(1))
	fmt.Printf("Restored to State: %s\n", originator.getState())

	originator.restoreMemento(caretaker.getMemento(0))
	fmt.Printf("Restored to State: %s\n", originator.getState())

}

originator Current State: A
originator Current State: B
originator Current State: C
Restored to State: B
Restored to State: A
```

### php

```php
<?php

namespace RefactoringGuru\Memento\Conceptual;

/**
 * يحتفظ المُنشئ ببعض الحالة المهمّة التي قد تتغيّر بمرور الوقت. كما يعرّف دالةً لحفظ
 * الحالة داخل تذكار ودالةً أخرى لاستعادة الحالة منه.
 */
class Originator
{
    /**
     * @var string من أجل التبسيط، تُخزَّن حالة المُنشئ داخل متغيّر واحد.
     */
    private $state;

    public function __construct(string $state)
    {
        $this->state = $state;
        echo "Originator: My initial state is: {$this->state}\n";
    }

    /**
     * قد يؤثّر منطق الأعمال الخاص بالمُنشئ في حالته الداخلية. ولذلك ينبغي على العميل أخذ
     * نسخة احتياطية من الحالة قبل استدعاء دوالّ منطق الأعمال عبر الدالة save().
     */
    public function doSomething(): void
    {
        echo "Originator: I'm doing something important.\n";
        $this->state = $this->generateRandomString(30);
        echo "Originator: and my state has changed to: {$this->state}\n";
    }

    private function generateRandomString(int $length = 10): string
    {
        return substr(
            str_shuffle(
                str_repeat(
                    $x = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
                    ceil($length / strlen($x))
                )
            ),
            1,
            $length,
        );
    }

    /**
     * يحفظ الحالة الحالية داخل تذكار.
     */
    public function save(): Memento
    {
        return new ConcreteMemento($this->state);
    }

    /**
     * يستعيد حالة المُنشئ من كائن تذكار.
     */
    public function restore(Memento $memento): void
    {
        $this->state = $memento->getState();
        echo "Originator: My state has changed to: {$this->state}\n";
    }
}

/**
 * توفّر واجهة التذكار طريقةً لاسترجاع البيانات الوصفية للتذكار، مثل تاريخ الإنشاء أو
 * الاسم. غير أنها لا تكشف حالة المُنشئ.
 */
interface Memento
{
    public function getName(): string;

    public function getDate(): string;
}

/**
 * يحتوي التذكار المحسوس (Concrete Memento) على البنية اللازمة لتخزين حالة المُنشئ.
 */
class ConcreteMemento implements Memento
{
    private $state;

    private $date;

    public function __construct(string $state)
    {
        $this->state = $state;
        $this->date = date('Y-m-d H:i:s');
    }

    /**
     * يستخدم المُنشئ هذه الدالة عند استعادة حالته.
     */
    public function getState(): string
    {
        return $this->state;
    }

    /**
     * تُستخدم بقية الدوالّ من قِبَل الوصيّ لعرض البيانات الوصفية.
     */
    public function getName(): string
    {
        return $this->date . " / (" . substr($this->state, 0, 9) . "...)";
    }

    public function getDate(): string
    {
        return $this->date;
    }
}

/**
 * لا يعتمد الوصيّ على فئة التذكار المحسوس. ولذلك لا يملك وصولًا إلى حالة المُنشئ المخزّنة
 * داخل التذكار. وهو يتعامل مع جميع التذكارات عبر واجهة التذكار الأساسية.
 */
class Caretaker
{
    /**
     * @var Memento[]
     */
    private $mementos = [];

    /**
     * @var Originator
     */
    private $originator;

    public function __construct(Originator $originator)
    {
        $this->originator = $originator;
    }

    public function backup(): void
    {
        echo "\nCaretaker: Saving Originator's state...\n";
        $this->mementos[] = $this->originator->save();
    }

    public function undo(): void
    {
        if (!count($this->mementos)) {
            return;
        }
        $memento = array_pop($this->mementos);

        echo "Caretaker: Restoring state to: " . $memento->getName() . "\n";
        try {
            $this->originator->restore($memento);
        } catch (\Exception $e) {
            $this->undo();
        }
    }

    public function showHistory(): void
    {
        echo "Caretaker: Here's the list of mementos:\n";
        foreach ($this->mementos as $memento) {
            echo $memento->getName() . "\n";
        }
    }
}

/**
 * كود العميل.
 */
$originator = new Originator("Super-duper-super-puper-super.");
$caretaker = new Caretaker($originator);

$caretaker->backup();
$originator->doSomething();

$caretaker->backup();
$originator->doSomething();

$caretaker->backup();
$originator->doSomething();

echo "\n";
$caretaker->showHistory();

echo "\nClient: Now, let's rollback!\n\n";
$caretaker->undo();

echo "\nClient: Once more!\n\n";
$caretaker->undo();

Originator: My initial state is: Super-duper-super-puper-super. 

Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: srGIngezAEboNPDjBkuvymJKUtMSFX

Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: UwCZQaHJOiERLlchyVuMbXNtpqTgWF

Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: incqsdoJXkbDUuVOvRFYyKBgfzwZCQ

Caretaker: Here's the list of mementos:
2018-06-04 14:50:39 / (Super-dup...)
2018-06-04 14:50:39 / (srGIngezA...)
2018-06-04 14:50:39 / (UwCZQaHJO...)

Client: Now, let's rollback!

Caretaker: Restoring state to: 2018-06-04 14:50:39 / (UwCZQaHJO...)
Originator: My state has changed to: UwCZQaHJOiERLlchyVuMbXNtpqTgWF

Client: Once more!

Caretaker: Restoring state to: 2018-06-04 14:50:39 / (srGIngezA...)
Originator: My state has changed to: srGIngezAEboNPDjBkuvymJKUtMSFX

<?php

namespace RefactoringGuru\Memento\RealWorld;

/**
 * يحتفظ المُنشئ ببعض الحالة المهمّة التي قد تتغيّر بمرور الوقت. كما يعرّف دالةً لحفظ
 * الحالة داخل تذكار ودالةً أخرى لاستعادة الحالة منه.
 *
 * في هذا المثال، يؤدّي ConfigManager دور المُنشئ. فهو يدير إعدادات تهيئة التطبيق التي
 * يمكن تعديلها أثناء التشغيل. وقد تتضمّن الإعدادات إعدادات قاعدة البيانات، وأعلام الميزات
 * (feature flags)، وسمات واجهة الاستخدام، وإعدادات تحسين محركات البحث (SEO)، وغيرها من
 * معاملات التطبيق.
 *
 * قد يؤثّر منطق الأعمال الخاص بـ ConfigManager في حالته الداخلية. ولذلك ينبغي على العميل
 * أخذ نسخة احتياطية من الحالة قبل استدعاء دوالّ منطق الأعمال عبر الدالة save().
 */
class ConfigManager
{
    /**
     * @var array
     * من أجل التبسيط، تُخزَّن حالة التهيئة داخل مصفوفة. وفي التطبيقات الحقيقية قد تكون
     * بنيةً أكثر تعقيدًا تتضمّن التحقّق من الصحّة، وفحص الأنواع، وإعدادات متداخلة.
     */
    private $config;

    /**
     * يهيّئ الباني مدير التهيئة بالإعدادات الافتراضية.
     *
     * @param array $initialConfig قيم التهيئة الأوّلية
     */
    public function __construct(array $initialConfig)
    {
        $this->config = $initialConfig;
        echo "ConfigManager: Initialized with " . count($initialConfig) . " config items.\n";
    }

    /**
     * قد يؤثّر منطق الأعمال الخاص بـ ConfigManager في حالته الداخلية. ولذلك ينبغي على
     * العميل أخذ نسخة احتياطية من الحالة قبل استدعاء دوالّ منطق الأعمال عبر الدالة
     * save().
     *
     * تحاكي هذه الدالة تحديث قيم التهيئة، وهي عملية شائعة في تطبيقات الويب (لوحات
     * الإدارة، تفضيلات المستخدم، وما إلى ذلك).
     *
     * @param array $newValues قيم تهيئة جديدة لدمجها مع التهيئة الحالية
     */
    public function updateConfig(array $newValues): void
    {
        echo "ConfigManager: Updating configuration with new values...\n";
        $this->config = array_merge($this->config, $newValues);
        echo "ConfigManager: Configuration updated. Current config has " . count($this->config) . " items.\n";
    }

    /**
     * يسترجع حالة التهيئة الحالية.
     *
     * @return array التهيئة الحالية
     */
    public function getConfig(): array
    {
        return $this->config;
    }

    /**
     * يحفظ الحالة الحالية داخل تذكار.
     *
     * تنشئ هذه الدالة لقطةً من حالة التهيئة الحالية وتعيدها مغلَّفة داخل كائن تذكار.
     * ويحتوي التذكار على كل المعلومات اللازمة لاستعادة التهيئة إلى حالتها الحالية لاحقًا.
     *
     * @return ConfigSnapshot تذكار يحتوي على حالة التهيئة الحالية
     */
    public function save(): ConfigSnapshot
    {
        echo "ConfigManager: Saving current configuration state...\n";
        return new ConfigSnapshot($this->config);
    }

    /**
     * يستعيد حالة ConfigManager من كائن تذكار.
     *
     * تأخذ هذه الدالة تذكارًا وتستعيد التهيئة إلى الحالة التي حُفِظت في ذلك التذكار. وهذا
     * مفيد لتنفيذ ميزة التراجع أو للتراجع عن تغييرات تهيئة فاشلة.
     *
     * @param ConfigSnapshot $snapshot التذكار الذي ستُستعاد منه الحالة
     */
    public function restore(ConfigSnapshot $snapshot): void
    {
        $this->config = $snapshot->getState();
        echo "ConfigManager: Configuration restored from snapshot.\n";
    }

    /**
     * دالة أعمال إضافية: إعادة التعيين إلى الإعدادات الافتراضية
     *
     * تعيد التهيئة إلى القيم الافتراضية. وهي عملية أخرى قد تستفيد من إنشاء نسخة احتياطية
     * قبل التنفيذ.
     */
    public function resetToDefaults(): void
    {
        echo "ConfigManager: Resetting configuration to defaults...\n";
        $this->config = [
            'maintenance_mode' => false,
            'theme' => 'light',
            'debug' => false
        ];
        echo "ConfigManager: Configuration reset to defaults.\n";
    }

    /**
     * دالة أعمال إضافية: تفعيل وضع الصيانة
     *
     * تفعّل وضع الصيانة بسرعة على مستوى التطبيق بأكمله.
     */
    public function enableMaintenanceMode(): void
    {
        echo "ConfigManager: Enabling maintenance mode...\n";
        $this->config['maintenance_mode'] = true;
        $this->config['maintenance_message'] = 'System under maintenance. Please try again later.';
        echo "ConfigManager: Maintenance mode enabled.\n";
    }
}

/**
 * توفّر واجهة التذكار طريقةً لاسترجاع البيانات الوصفية للتذكار، مثل تاريخ الإنشاء أو
 * الاسم. غير أنها لا تكشف حالة المُنشئ.
 *
 * تضمن هذه الواجهة أن الفئات الخارجية يمكنها التعامل مع التذكارات دون وصول مباشر إلى
 * الحالة المخزّنة. وينبغي ألا يكون قادرًا على استخراج بيانات الحالة الفعلية إلا المُنشئ.
 */
interface ConfigMemento
{
    /**
     * يعيد اسمًا سهل الفهم لهذا التذكار.
     *
     * @return string اسم وصفي لهذه اللقطة
     */
    public function getName(): string;

    /**
     * يعيد تاريخ إنشاء هذا التذكار.
     *
     * @return string الطابع الزمني للإنشاء
     */
    public function getDate(): string;
}

/**
 * يحتوي التذكار المحسوس (Concrete Memento) على البنية اللازمة لتخزين حالة المُنشئ.
 *
 * تخزّن هذه الفئة لقطةً من حالة التهيئة مع بيانات وصفية عن وقت إنشاء اللقطة. وتُخزَّن
 * الحالة الفعلية بشكل خاص ولا يمكن الوصول إليها إلا من قِبَل المُنشئ عبر الدالة
 * getState().
 */
class ConfigSnapshot implements ConfigMemento
{
    /**
     * @var array حالة التهيئة وقت إنشاء هذه اللقطة
     */
    private $state;

    /**
     * @var string الطابع الزمني لوقت إنشاء هذه اللقطة
     */
    private $date;

    /**
     * يخزّن الباني الحالة المقدَّمة ويسجّل الطابع الزمني الحالي.
     *
     * @param array $state حالة التهيئة المراد تخزينها
     */
    public function __construct(array $state)
    {
        $this->state = $state;
        $this->date = date('Y-m-d H:i:s');
        echo "ConfigSnapshot: Created snapshot with " . count($state) . " config items.\n";
    }

    /**
     * يستخدم المُنشئ هذه الدالة عند استعادة حالته.
     *
     * توفّر هذه الدالة الوصول إلى بيانات الحالة المخزّنة. وينبغي ألا تُستدعى إلا من قِبَل
     * ConfigManager عند استعادة التهيئة.
     *
     * @return array حالة التهيئة المخزّنة
     */
    public function getState(): array
    {
        return $this->state;
    }

    /**
     * تُستخدم بقية الدوالّ من قِبَل الوصيّ لعرض البيانات الوصفية.
     *
     * يعيد اسمًا وصفيًا يتضمّن الطابع الزمني ومعاينةً لمحتوى التهيئة.
     *
     * @return string اسم سهل الفهم لهذه اللقطة
     */
    public function getName(): string
    {
        $configCount = count($this->state);
        $maintenanceStatus = $this->state['maintenance_mode'] ?? 'unknown';
        return $this->date . " / ({$configCount} items, maintenance: {$maintenanceStatus})";
    }

    /**
     * يعيد تاريخ إنشاء هذه اللقطة.
     *
     * @return string الطابع الزمني لوقت إنشاء هذه اللقطة
     */
    public function getDate(): string
    {
        return $this->date;
    }
}

/**
 * لا يعتمد الوصيّ على فئة التذكار المحسوس. ولذلك لا يملك وصولًا إلى حالة المُنشئ المخزّنة
 * داخل التذكار. وهو يتعامل مع جميع التذكارات عبر واجهة التذكار الأساسية.
 *
 * تدير فئة ConfigHistory مجموعةً من لقطات التهيئة وتوفّر ميزة التراجع. وهي توضّح كيف يمكن
 * للوصيّ إدارة التذكارات دون معرفة بنيتها الداخلية.
 */
class ConfigHistory
{
    /**
     * @var ConfigSnapshot[] مصفوفة من لقطات التهيئة المخزّنة
     */
    private $snapshots = [];

    /**
     * @var ConfigManager مرجع إلى مدير التهيئة
     */
    private $configManager;

    /**
     * ينشئ الباني العلاقة مع المُنشئ.
     *
     * @param ConfigManager $configManager مدير التهيئة المراد العمل معه
     */
    public function __construct(ConfigManager $configManager)
    {
        $this->configManager = $configManager;
        echo "ConfigHistory: History manager initialized.\n";
    }

    /**
     * ينشئ نسخة احتياطية من حالة التهيئة الحالية.
     *
     * تطلب هذه الدالة من المُنشئ إنشاء تذكار وتخزّنه في السجلّ. وينبغي استدعاؤها قبل
     * إجراء تغييرات قد تحتاج إلى التراجع عنها.
     */
    public function backup(): void
    {
        echo "\nConfigHistory: Creating backup of current configuration...\n";
        $this->snapshots[] = $this->configManager->save();
        echo "ConfigHistory: Backup created. Total backups: " . count($this->snapshots) . "\n";
    }

    /**
     * يستعيد التهيئة إلى أحدث نسخة احتياطية.
     *
     * تجلب هذه الدالة أحدث تذكار من السجلّ وتطلب من المُنشئ استعادة حالته من ذلك التذكار.
     */
    public function undo(): void
    {
        if (!count($this->snapshots)) {
            echo "ConfigHistory: No backups available for undo.\n";
            return;
        }

        $memento = array_pop($this->snapshots);

        echo "ConfigHistory: Restoring configuration to: " . $memento->getName() . "\n";
        try {
            $this->configManager->restore($memento);
            echo "ConfigHistory: Undo completed successfully.\n";
        } catch (\Exception $e) {
            echo "ConfigHistory: Undo failed, trying previous backup...\n";
            $this->undo();
        }
    }

    /**
     * يعرض سجلّ جميع لقطات التهيئة المحفوظة.
     *
     * تُظهر هذه الدالة جميع النسخ الاحتياطية المتاحة باستخدام المعلومات المتوفّرة عبر
     * واجهة التذكار فقط، دون الوصول إلى بيانات التهيئة الفعلية.
     */
    public function showHistory(): void
    {
        echo "\nConfigHistory: Available configuration backups:\n";
        if (empty($this->snapshots)) {
            echo "No backups available.\n";
        } else {
            foreach ($this->snapshots as $index => $memento) {
                echo "[{$index}] " . $memento->getName() . "\n";
            }
        }
        echo "\n";
    }

    /**
     * يمسح جميع النسخ الاحتياطية المخزّنة.
     *
     * تزيل هذه الدالة جميع التذكارات من السجلّ، وهو ما قد يكون مفيدًا عند البدء من جديد
     * أو لتحرير الذاكرة.
     */
    public function clearHistory(): void
    {
        $count = count($this->snapshots);
        $this->snapshots = [];
        echo "ConfigHistory: Cleared {$count} backups from history.\n";
    }

    /**
     * يحصل على عدد النسخ الاحتياطية المتاحة.
     *
     * @return int عدد النسخ الاحتياطية المخزّنة
     */
    public function getBackupCount(): int
    {
        return count($this->snapshots);
    }
}



/**
 * ============================================================================
 * كود العميل - أمثلة توضيحية وحالات استخدام
 * ============================================================================
 */

echo "=== Configuration Manager with Memento Pattern Demo ===\n\n";

/**
 * المثال 1: إدارة تهيئة أساسية مع النسخ الاحتياطي والاستعادة
 */
echo "--- Example 1: Basic Configuration Management ---\n";

// تهيئة مدير التهيئة بالإعدادات الافتراضية
$config = new ConfigManager([
    'maintenance_mode' => false,
    'theme' => 'light',
    'seo' => ['title' => 'My Website', 'description' => 'Welcome to my site!'],
    'debug' => false,
    'max_users' => 1000
]);

// إنشاء مدير السجلّ
$history = new ConfigHistory($config);

echo "\nInitial configuration:\n";
print_r($config->getConfig());

/**
 * المثال 2: إجراء تغييرات مع نسخ احتياطية
 */
echo "\n--- Example 2: Making Changes with Backups ---\n";

// إنشاء نسخة احتياطية قبل إجراء التغييرات
$history->backup();

// تحديث إعدادات السمة
$config->updateConfig([
    'theme' => 'dark',
    'theme_options' => ['sidebar' => 'collapsed', 'font_size' => 'large']
]);

echo "\nAfter theme update:\n";
print_r($config->getConfig());

// إنشاء نسخة احتياطية أخرى
$history->backup();

// تفعيل وضع الصيانة
$config->enableMaintenanceMode();

echo "\nAfter enabling maintenance mode:\n";
print_r($config->getConfig());

/**
 * المثال 3: عرض ميزة التراجع
 */
echo "\n--- Example 3: Undo Functionality ---\n";

// عرض السجلّ الحالي
$history->showHistory();

// التراجع عن آخر تغيير (وضع الصيانة)
echo "Undoing maintenance mode activation...\n";
$history->undo();

echo "\nAfter first undo:\n";
print_r($config->getConfig());

// التراجع عن تغييرات السمة
echo "\nUndoing theme changes...\n";
$history->undo();

echo "\nAfter second undo (back to original):\n";
print_r($config->getConfig());

/**
 * المثال 4: سيناريوهات تهيئة متعدّدة
 */
echo "\n--- Example 4: Multiple Configuration Scenarios ---\n";

// السيناريو أ: تهيئة تحسين محركات البحث (SEO)
$history->backup();
echo "\nScenario A: Updating SEO settings...\n";
$config->updateConfig([
    'seo' => [
        'title' => 'Best Products Online',
        'description' => 'Find the best products at great prices!',
        'keywords' => 'products, online, shopping, deals'
    ],
    'analytics' => ['google_id' => 'GA-123456', 'facebook_pixel' => 'FB-789012']
]);

echo "SEO configuration updated:\n";
print_r($config->getConfig());

// السيناريو ب: إعدادات الأداء
$history->backup();
echo "\nScenario B: Updating performance settings...\n";
$config->updateConfig([
    'cache_enabled' => true,
    'cache_duration' => 3600,
    'compression' => 'gzip',
    'max_users' => 2000
]);

echo "Performance settings updated:\n";
print_r($config->getConfig());

// السيناريو ج: تراجع طارئ
echo "\nScenario C: Emergency rollback to SEO-only changes...\n";
$history->undo(); // إزالة تغييرات الأداء
echo "Rolled back performance changes:\n";
print_r($config->getConfig());

/**
 * المثال 5: إعادة التعيين والاسترداد
 */
echo "\n--- Example 5: Reset and Recovery ---\n";

// حفظ الحالة الحالية قبل إعادة التعيين
$history->backup();

// إعادة تعيين التهيئة
$config->resetToDefaults();

echo "\nAfter reset to defaults:\n";
print_r($config->getConfig());

// استعادة التهيئة السابقة
echo "\nRestoring previous configuration...\n";
$history->undo();

echo "\nAfter restoration:\n";
print_r($config->getConfig());

/**
 * المثال 6: إدارة السجلّ
 */
echo "\n--- Example 6: History Management ---\n";

// عرض السجلّ الكامل
$history->showHistory();

echo "Total backups available: " . $history->getBackupCount() . "\n";

// مسح السجلّ
echo "\nClearing history...\n";
$history->clearHistory();

// عرض السجلّ بعد المسح
$history->showHistory();

/**
 * المثال 7: محاكاة سير عمل من العالم الواقعي
 */
echo "\n--- Example 7: Real-World Workflow Simulation ---\n";

// محاكاة سير عمل نموذجي لتحديث التهيئة
echo "Simulating typical admin workflow...\n\n";

// الخطوة 1: يريد المسؤول تحديث الموقع لأجل عرض ترويجي
$history->backup();
echo "Step 1: Preparing for Black Friday promotion...\n";
$config->updateConfig([
    'promotion_banner' => 'Black Friday Sale - 50% Off!',
    'theme' => 'dark',
    'special_offers' => ['discount' => 50, 'code' => 'BLACKFRIDAY50']
]);

// الخطوة 2: تحديث تحسين محركات البحث لأجل العرض الترويجي
$history->backup();
echo "\nStep 2: Updating SEO for promotion visibility...\n";
$config->updateConfig([
    'seo' => [
        'title' => 'Black Friday Sale - 50% Off Everything!',
        'description' => 'Huge Black Friday discounts on all products. Limited time offer!',
        'keywords' => 'black friday, sale, discount, deals, promotion'
    ]
]);

// الخطوة 3: حدث خطأ ما، يلزم التراجع عن تغييرات تحسين محركات البحث فقط
echo "\nStep 3: SEO changes caused issues, rolling back SEO only...\n";
$history->undo();

echo "Final configuration after workflow:\n";
print_r($config->getConfig());

echo "\nFinal history state:\n";
$history->showHistory();

=== Configuration Manager with Memento Pattern Demo ===

--- Example 1: Basic Configuration Management ---
ConfigManager: Initialized with 5 config items.
ConfigHistory: History manager initialized.

Initial configuration:
Array
(
    [maintenance_mode] => 
    [theme] => light
    [seo] => Array
        (
            [title] => My Website
            [description] => Welcome to my site!
        )

    [debug] => 
    [max_users] => 1000
)

--- Example 2: Making Changes with Backups ---

ConfigHistory: Creating backup of current configuration...
ConfigManager: Saving current configuration state...
ConfigSnapshot: Created snapshot with 5 config items.
ConfigHistory: Backup created. Total backups: 1
ConfigManager: Updating configuration with new values...
ConfigManager: Configuration updated. Current config has 6 items.

After theme update:
Array
(
    [maintenance_mode] => 
    [theme] => dark
    [seo] => Array
        (
            [title] => My Website
            [description] => Welcome to my site!
        )

    [debug] => 
    [max_users] => 1000
    [theme_options] => Array
        (
            [sidebar] => collapsed
            [font_size] => large
        )

)

ConfigHistory: Creating backup of current configuration...
ConfigManager: Saving current configuration state...
ConfigSnapshot: Created snapshot with 6 config items.
ConfigHistory: Backup created. Total backups: 2
ConfigManager: Enabling maintenance mode...
ConfigManager: Maintenance mode enabled.

After enabling maintenance mode:
Array
(
    [maintenance_mode] => 1
    [theme] => dark
    [seo] => Array
        (
            [title] => My Website
            [description] => Welcome to my site!
        )

    [debug] => 
    [max_users] => 1000
    [theme_options] => Array
        (
            [sidebar] => collapsed
            [font_size] => large
        )

    [maintenance_message] => System under maintenance. Please try again later.
)

--- Example 3: Undo Functionality ---

ConfigHistory: Available configuration backups:
[0] 2025-07-28 15:04:16 / (5 items, maintenance: )
[1] 2025-07-28 15:04:16 / (6 items, maintenance: )

Undoing maintenance mode activation...
ConfigHistory: Restoring configuration to: 2025-07-28 15:04:16 / (6 items, maintenance: )
ConfigManager: Configuration restored from snapshot.
ConfigHistory: Undo completed successfully.

After first undo:
Array
(
    [maintenance_mode] => 
    [theme] => dark
    [seo] => Array
        (
            [title] => My Website
            [description] => Welcome to my site!
        )

    [debug] => 
    [max_users] => 1000
    [theme_options] => Array
        (
            [sidebar] => collapsed
            [font_size] => large
        )

)

Undoing theme changes...
ConfigHistory: Restoring configuration to: 2025-07-28 15:04:16 / (5 items, maintenance: )
ConfigManager: Configuration restored from snapshot.
ConfigHistory: Undo completed successfully.

After second undo (back to original):
Array
(
    [maintenance_mode] => 
    [theme] => light
    [seo] => Array
        (
            [title] => My Website
            [description] => Welcome to my site!
        )

    [debug] => 
    [max_users] => 1000
)

--- Example 4: Multiple Configuration Scenarios ---

ConfigHistory: Creating backup of current configuration...
ConfigManager: Saving current configuration state...
ConfigSnapshot: Created snapshot with 5 config items.
ConfigHistory: Backup created. Total backups: 1

Scenario A: Updating SEO settings...
ConfigManager: Updating configuration with new values...
ConfigManager: Configuration updated. Current config has 6 items.
SEO configuration updated:
Array
(
    [maintenance_mode] => 
    [theme] => light
    [seo] => Array
        (
            [title] => Best Products Online
            [description] => Find the best products at great prices!
            [keywords] => products, online, shopping, deals
        )

    [debug] => 
    [max_users] => 1000
    [analytics] => Array
        (
            [google_id] => GA-123456
            [facebook_pixel] => FB-789012
        )

)

ConfigHistory: Creating backup of current configuration...
ConfigManager: Saving current configuration state...
ConfigSnapshot: Created snapshot with 6 config items.
ConfigHistory: Backup created. Total backups: 2

Scenario B: Updating performance settings...
ConfigManager: Updating configuration with new values...
ConfigManager: Configuration updated. Current config has 9 items.
Performance settings updated:
Array
(
    [maintenance_mode] => 
    [theme] => light
    [seo] => Array
        (
            [title] => Best Products Online
            [description] => Find the best products at great prices!
            [keywords] => products, online, shopping, deals
        )

    [debug] => 
    [max_users] => 2000
    [analytics] => Array
        (
            [google_id] => GA-123456
            [facebook_pixel] => FB-789012
        )

    [cache_enabled] => 1
    [cache_duration] => 3600
    [compression] => gzip
)

Scenario C: Emergency rollback to SEO-only changes...
ConfigHistory: Restoring configuration to: 2025-07-28 15:04:16 / (6 items, maintenance: )
ConfigManager: Configuration restored from snapshot.
ConfigHistory: Undo completed successfully.
Rolled back performance changes:
Array
(
    [maintenance_mode] => 
    [theme] => light
    [seo] => Array
        (
            [title] => Best Products Online
            [description] => Find the best products at great prices!
            [keywords] => products, online, shopping, deals
        )

    [debug] => 
    [max_users] => 1000
    [analytics] => Array
        (
            [google_id] => GA-123456
            [facebook_pixel] => FB-789012
        )

)

--- Example 5: Reset and Recovery ---

ConfigHistory: Creating backup of current configuration...
ConfigManager: Saving current configuration state...
ConfigSnapshot: Created snapshot with 6 config items.
ConfigHistory: Backup created. Total backups: 2
ConfigManager: Resetting configuration to defaults...
ConfigManager: Configuration reset to defaults.

After reset to defaults:
Array
(
    [maintenance_mode] => 
    [theme] => light
    [debug] => 
)

Restoring previous configuration...
ConfigHistory: Restoring configuration to: 2025-07-28 15:04:16 / (6 items, maintenance: )
ConfigManager: Configuration restored from snapshot.
ConfigHistory: Undo completed successfully.

After restoration:
Array
(
    [maintenance_mode] => 
    [theme] => light
    [seo] => Array
        (
            [title] => Best Products Online
            [description] => Find the best products at great prices!
            [keywords] => products, online, shopping, deals
        )

    [debug] => 
    [max_users] => 1000
    [analytics] => Array
        (
            [google_id] => GA-123456
            [facebook_pixel] => FB-789012
        )

)

--- Example 6: History Management ---

ConfigHistory: Available configuration backups:
[0] 2025-07-28 15:04:16 / (5 items, maintenance: )

Total backups available: 1

Clearing history...
ConfigHistory: Cleared 1 backups from history.

ConfigHistory: Available configuration backups:
No backups available.


--- Example 7: Real-World Workflow Simulation ---
Simulating typical admin workflow...


ConfigHistory: Creating backup of current configuration...
ConfigManager: Saving current configuration state...
ConfigSnapshot: Created snapshot with 6 config items.
ConfigHistory: Backup created. Total backups: 1
Step 1: Preparing for Black Friday promotion...
ConfigManager: Updating configuration with new values...
ConfigManager: Configuration updated. Current config has 8 items.

ConfigHistory: Creating backup of current configuration...
ConfigManager: Saving current configuration state...
ConfigSnapshot: Created snapshot with 8 config items.
ConfigHistory: Backup created. Total backups: 2

Step 2: Updating SEO for promotion visibility...
ConfigManager: Updating configuration with new values...
ConfigManager: Configuration updated. Current config has 8 items.

Step 3: SEO changes caused issues, rolling back SEO only...
ConfigHistory: Restoring configuration to: 2025-07-28 15:04:16 / (8 items, maintenance: )
ConfigManager: Configuration restored from snapshot.
ConfigHistory: Undo completed successfully.
Final configuration after workflow:
Array
(
    [maintenance_mode] => 
    [theme] => dark
    [seo] => Array
        (
            [title] => Best Products Online
            [description] => Find the best products at great prices!
            [keywords] => products, online, shopping, deals
        )

    [debug] => 
    [max_users] => 1000
    [analytics] => Array
        (
            [google_id] => GA-123456
            [facebook_pixel] => FB-789012
        )

    [promotion_banner] => Black Friday Sale - 50% Off!
    [special_offers] => Array
        (
            [discount] => 50
            [code] => BLACKFRIDAY50
        )

)

Final history state:

ConfigHistory: Available configuration backups:
[0] 2025-07-28 15:04:16 / (6 items, maintenance: )
```

### python

```python
from __future__ import annotations
from abc import ABC, abstractmethod
from datetime import datetime
from random import sample
from string import ascii_letters


class Originator:
    """
    يحتفظ المُنشئ ببعض الحالة المهمّة التي قد تتغيّر بمرور الوقت. كما يعرّف دالةً لحفظ
    الحالة داخل تذكار ودالةً أخرى لاستعادة الحالة منه.
    """

    _state = None
    """
    من أجل التبسيط، تُخزَّن حالة المُنشئ داخل متغيّر واحد.
    """

    def __init__(self, state: str) -> None:
        self._state = state
        print(f"Originator: My initial state is: {self._state}")

    def do_something(self) -> None:
        """
        قد يؤثّر منطق الأعمال الخاص بالمُنشئ في حالته الداخلية. ولذلك ينبغي على العميل أخذ
        نسخة احتياطية من الحالة قبل استدعاء دوالّ منطق الأعمال عبر الدالة save().
        """

        print("Originator: I'm doing something important.")
        self._state = self._generate_random_string(30)
        print(f"Originator: and my state has changed to: {self._state}")

    @staticmethod
    def _generate_random_string(length: int = 10) -> str:
        return "".join(sample(ascii_letters, length))

    def save(self) -> Memento:
        """
        يحفظ الحالة الحالية داخل تذكار.
        """

        return ConcreteMemento(self._state)

    def restore(self, memento: Memento) -> None:
        """
        يستعيد حالة المُنشئ من كائن تذكار.
        """

        self._state = memento.get_state()
        print(f"Originator: My state has changed to: {self._state}")


class Memento(ABC):
    """
    توفّر واجهة التذكار طريقةً لاسترجاع البيانات الوصفية للتذكار، مثل تاريخ الإنشاء أو
    الاسم. غير أنها لا تكشف حالة المُنشئ.
    """

    @abstractmethod
    def get_name(self) -> str:
        pass

    @abstractmethod
    def get_date(self) -> str:
        pass


class ConcreteMemento(Memento):
    def __init__(self, state: str) -> None:
        self._state = state
        self._date = str(datetime.now())[:19]

    def get_state(self) -> str:
        """
        يستخدم المُنشئ هذه الدالة عند استعادة حالته.
        """
        return self._state

    def get_name(self) -> str:
        """
        تُستخدم بقية الدوالّ من قِبَل الوصيّ لعرض البيانات الوصفية.
        """

        return f"{self._date} / ({self._state[0:9]}...)"

    def get_date(self) -> str:
        return self._date


class Caretaker:
    """
    لا يعتمد الوصيّ على فئة التذكار المحسوس. ولذلك لا يملك وصولًا إلى حالة المُنشئ
    المخزّنة داخل التذكار. وهو يتعامل مع جميع التذكارات عبر واجهة التذكار الأساسية.
    """

    def __init__(self, originator: Originator) -> None:
        self._mementos = []
        self._originator = originator

    def backup(self) -> None:
        print("\nCaretaker: Saving Originator's state...")
        self._mementos.append(self._originator.save())

    def undo(self) -> None:
        if not len(self._mementos):
            return

        memento = self._mementos.pop()
        print(f"Caretaker: Restoring state to: {memento.get_name()}")
        try:
            self._originator.restore(memento)
        except Exception:
            self.undo()

    def show_history(self) -> None:
        print("Caretaker: Here's the list of mementos:")
        for memento in self._mementos:
            print(memento.get_name())


if __name__ == "__main__":
    originator = Originator("Super-duper-super-puper-super.")
    caretaker = Caretaker(originator)

    caretaker.backup()
    originator.do_something()

    caretaker.backup()
    originator.do_something()

    caretaker.backup()
    originator.do_something()

    print()
    caretaker.show_history()

    print("\nClient: Now, let's rollback!\n")
    caretaker.undo()

    print("\nClient: Once more!\n")
    caretaker.undo()

Originator: My initial state is: Super-duper-super-puper-super.

Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: wQAehHYOqVSlpEXjyIcgobrxsZUnat

Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: lHxNORKcsgMWYnJqoXjVCbQLEIeiSp

Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: cvIYsRilNOtwynaKdEZpDCQkFAXVMf

Caretaker: Here's the list of mementos:
2019-01-26 21:11:24 / (Super-dup...)
2019-01-26 21:11:24 / (wQAehHYOq...)
2019-01-26 21:11:24 / (lHxNORKcs...)

Client: Now, let's rollback!

Caretaker: Restoring state to: 2019-01-26 21:11:24 / (lHxNORKcs...)
Originator: My state has changed to: lHxNORKcsgMWYnJqoXjVCbQLEIeiSp

Client: Once more!

Caretaker: Restoring state to: 2019-01-26 21:11:24 / (wQAehHYOq...)
Originator: My state has changed to: wQAehHYOqVSlpEXjyIcgobrxsZUnat
```

### ruby

```ruby
# يحتفظ المُنشئ ببعض الحالة المهمّة التي قد تتغيّر بمرور الوقت. كما يعرّف دالةً لحفظ
# الحالة داخل تذكار ودالةً أخرى لاستعادة الحالة منه.
class Originator
  # من أجل التبسيط، تُخزَّن حالة المُنشئ داخل متغيّر واحد.
  attr_accessor :state
  private :state

  # @param [String] state
  def initialize(state)
    @state = state
    puts "Originator: My initial state is: #{@state}"
  end

  # قد يؤثّر منطق الأعمال الخاص بالمُنشئ في حالته الداخلية. ولذلك ينبغي على العميل أخذ
  # نسخة احتياطية من الحالة قبل استدعاء دوالّ منطق الأعمال عبر الدالة save().
  def do_something
    puts 'Originator: I\'m doing something important.'
    @state = generate_random_string(30)
    puts "Originator: and my state has changed to: #{@state}"
  end

  private def generate_random_string(length = 10)
    ascii_letters = [*'a'..'z', *'A'..'Z']
    (0...length).map { ascii_letters.sample }.join
  end

  # يحفظ الحالة الحالية داخل تذكار.
  def save
    ConcreteMemento.new(@state)
  end

  # يستعيد حالة المُنشئ من كائن تذكار.
  def restore(memento)
    @state = memento.state
    puts "Originator: My state has changed to: #{@state}"
  end
end

# توفّر واجهة التذكار طريقةً لاسترجاع البيانات الوصفية للتذكار، مثل تاريخ الإنشاء أو
# الاسم. غير أنها لا تكشف حالة المُنشئ.
class Memento
  # @abstract
  #
  # @return [String]
  def name
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end

  # @abstract
  #
  # @return [String]
  def date
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end
end

class ConcreteMemento < Memento
  # @param [String] state
  def initialize(state)
    @state = state
    @date = Time.now.strftime('%F %T')
  end

  # يستخدم المُنشئ هذه الدالة عند استعادة حالته.
  attr_reader :state

  # تُستخدم بقية الدوالّ من قِبَل الوصيّ لعرض البيانات الوصفية.
  def name
    "#{@date} / (#{@state[0, 9]}...)"
  end

  # @return [String]
  attr_reader :date
end

# لا يعتمد الوصيّ على فئة التذكار المحسوس. ولذلك لا يملك وصولًا إلى حالة المُنشئ المخزّنة
# داخل التذكار. وهو يتعامل مع جميع التذكارات عبر واجهة التذكار الأساسية.
class Caretaker
  # @param [Originator] originator
  def initialize(originator)
    @mementos = []
    @originator = originator
  end

  def backup
    puts "\nCaretaker: Saving Originator's state..."
    @mementos << @originator.save
  end

  def undo
    return if @mementos.empty?

    memento = @mementos.pop
    puts "Caretaker: Restoring state to: #{memento.name}"

    begin
      @originator.restore(memento)
    rescue StandardError
      undo
    end
  end

  def show_history
    puts 'Caretaker: Here\'s the list of mementos:'

    @mementos.each { |memento| puts memento.name }
  end
end

originator = Originator.new('Super-duper-super-puper-super.')
caretaker = Caretaker.new(originator)

caretaker.backup
originator.do_something

caretaker.backup
originator.do_something

caretaker.backup
originator.do_something

puts "\n"
caretaker.show_history

puts "\nClient: Now, let's rollback!\n"
caretaker.undo

puts "\nClient: Once more!\n"
caretaker.undo

Originator: My initial state is: Super-duper-super-puper-super.

Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: CHYzYSIWbqvWkCzIHOqTyEJWfQlFMn

Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: vbkhwCeAEQBpLwQLlhmpcvUnwzxVnT

Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: SBWlQnAEPLsitiOQAZbGlXHZAeWBoW

Caretaker: Here's the list of mementos:
2023-08-11 15:02:35 / (Super-dup...)
2023-08-11 15:02:35 / (CHYzYSIWb...)
2023-08-11 15:02:35 / (vbkhwCeAE...)

Client: Now, let's rollback!
Caretaker: Restoring state to: 2023-08-11 15:02:35 / (vbkhwCeAE...)
Originator: My state has changed to: vbkhwCeAEQBpLwQLlhmpcvUnwzxVnT

Client: Once more!
Caretaker: Restoring state to: 2023-08-11 15:02:35 / (CHYzYSIWb...)
Originator: My state has changed to: CHYzYSIWbqvWkCzIHOqTyEJWfQlFMn
```

### rust

```rust
trait Memento<T> {
    fn restore(self) -> T;
    fn print(&self);
}

struct Originator {
    state: u32,
}

impl Originator {
    pub fn save(&self) -> OriginatorBackup {
        OriginatorBackup {
            state: self.state.to_string(),
        }
    }
}

struct OriginatorBackup {
    state: String,
}

impl Memento<Originator> for OriginatorBackup {
    fn restore(self) -> Originator {
        Originator {
            state: self.state.parse().unwrap(),
        }
    }

    fn print(&self) {
        println!("Originator backup: '{}'", self.state);
    }
}

fn main() {
    let mut history = Vec::<OriginatorBackup>::new();

    let mut originator = Originator { state: 0 };

    originator.state = 1;
    history.push(originator.save());

    originator.state = 2;
    history.push(originator.save());

    for moment in history.iter() {
        moment.print();
    }

    let originator = history.pop().unwrap().restore();
    println!("Restored to state: {}", originator.state);

    let originator = history.pop().unwrap().restore();
    println!("Restored to state: {}", originator.state);
}

Originator backup: '1'
Originator backup: '2'
Restored to state: 2
Restored to state: 1

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct Originator {
    state: u32,
}

use serde::{Deserialize, Serialize};

/// كائن مراد تخزينه. يشتقّ تنفيذًا افتراضيًا للسمتين `Serialize` و`Deserialize`، مما يتيح
/// تحويله إلى صِيَغ مختلفة كثيرة (مثل JSON).
#[derive(Serialize, Deserialize)]
struct Originator {
    state: u32,
}

impl Originator {
    /// يُسَلسِل (serialize) المُنشئ إلى سلسلة نصية بصيغة JSON.
    pub fn save(&self) -> String {
        serde_json::to_string(self).unwrap()
    }

    /// يفكّ تسلسل (deserialize) المُنشئ من سلسلة نصية بصيغة JSON.
    pub fn restore(json: &str) -> Self {
        serde_json::from_str(json).unwrap()
    }
}

fn main() {
    // مكدّسة من التذكارات.
    let mut history = Vec::<String>::new();

    let mut originator = Originator { state: 0 };

    originator.state = 1;
    history.push(originator.save());

    originator.state = 2;
    history.push(originator.save());

    for moment in history.iter() {
        println!("{}", moment);
    }

    let originator = Originator::restore(&history.pop().unwrap());
    println!("Restored to state: {}", originator.state);

    let originator = Originator::restore(&history.pop().unwrap());
    println!("Restored to state: {}", originator.state);
}

{"state":1}
{"state":2}
Restored to state: 2
Restored to state: 1
```

### swift

```swift
import XCTest

/// يحتفظ المُنشئ ببعض الحالة المهمّة التي قد تتغيّر بمرور الوقت. كما يعرّف دالةً لحفظ
/// الحالة داخل تذكار ودالةً أخرى لاستعادة الحالة منه.
class Originator {

    /// من أجل التبسيط، تُخزَّن حالة المُنشئ داخل متغيّر واحد.
    private var state: String

    init(state: String) {
        self.state = state
        print("Originator: My initial state is: \(state)")
    }

    /// قد يؤثّر منطق الأعمال الخاص بالمُنشئ في حالته الداخلية. ولذلك ينبغي على العميل أخذ
    /// نسخة احتياطية من الحالة قبل استدعاء دوالّ منطق الأعمال عبر الدالة save().
    func doSomething() {
        print("Originator: I'm doing something important.")
        state = generateRandomString()
        print("Originator: and my state has changed to: \(state)")
    }

    private func generateRandomString() -> String {
        return String(UUID().uuidString.suffix(4))
    }

    /// يحفظ الحالة الحالية داخل تذكار.
    func save() -> Memento {
        return ConcreteMemento(state: state)
    }

    /// يستعيد حالة المُنشئ من كائن تذكار.
    func restore(memento: Memento) {
        guard let memento = memento as? ConcreteMemento else { return }
        self.state = memento.state
        print("Originator: My state has changed to: \(state)")
    }
}

/// توفّر واجهة التذكار طريقةً لاسترجاع البيانات الوصفية للتذكار، مثل تاريخ الإنشاء أو
/// الاسم. غير أنها لا تكشف حالة المُنشئ.
protocol Memento {

    var name: String { get }
    var date: Date { get }
}

/// يحتوي التذكار المحسوس (Concrete Memento) على البنية اللازمة لتخزين حالة المُنشئ.
class ConcreteMemento: Memento {

    /// يستخدم المُنشئ هذه الدالة عند استعادة حالته.
    private(set) var state: String
    private(set) var date: Date

    init(state: String) {
        self.state = state
        self.date = Date()
    }

    /// تُستخدم بقية الدوالّ من قِبَل الوصيّ لعرض البيانات الوصفية.
    var name: String { return state + " " + date.description.suffix(14).prefix(8) }
}

/// لا يعتمد الوصيّ على فئة التذكار المحسوس. ولذلك لا يملك وصولًا إلى حالة المُنشئ
/// المخزّنة داخل التذكار. وهو يتعامل مع جميع التذكارات عبر واجهة التذكار الأساسية.
class Caretaker {

    private lazy var mementos = [Memento]()
    private var originator: Originator

    init(originator: Originator) {
        self.originator = originator
    }

    func backup() {
        print("\nCaretaker: Saving Originator's state...\n")
        mementos.append(originator.save())
    }

    func undo() {

        guard !mementos.isEmpty else { return }
        let removedMemento = mementos.removeLast()

        print("Caretaker: Restoring state to: " + removedMemento.name)
        originator.restore(memento: removedMemento)
    }

    func showHistory() {
        print("Caretaker: Here's the list of mementos:\n")
        mementos.forEach({ print($0.name) })
    }
}

/// لنرَ كيف يعمل كل شيء معًا.
class MementoConceptual: XCTestCase {

    func testMementoConceptual() {

        let originator = Originator(state: "Super-duper-super-puper-super.")
        let caretaker = Caretaker(originator: originator)

        caretaker.backup()
        originator.doSomething()

        caretaker.backup()
        originator.doSomething()

        caretaker.backup()
        originator.doSomething()

        print("\n")
        caretaker.showHistory()

        print("\nClient: Now, let's rollback!\n\n")
        caretaker.undo()

        print("\nClient: Once more!\n\n")
        caretaker.undo()
    }
}

Originator: My initial state is: Super-duper-super-puper-super.

Caretaker: Saving Originator's state...

Originator: I'm doing something important.
Originator: and my state has changed to: 1923

Caretaker: Saving Originator's state...

Originator: I'm doing something important.
Originator: and my state has changed to: 74FB

Caretaker: Saving Originator's state...

Originator: I'm doing something important.
Originator: and my state has changed to: 3681


Caretaker: Here's the list of mementos:

Super-duper-super-puper-super. 11:45:44
1923 11:45:44
74FB 11:45:44

Client: Now, let's rollback!


Caretaker: Restoring state to: 74FB 11:45:44
Originator: My state has changed to: 74FB

Client: Once more!


Caretaker: Restoring state to: 1923 11:45:44
Originator: My state has changed to: 1923

import XCTest

class MementoRealWorld: XCTestCase {

    /// كثيرًا ما يُستخدم نمط الحالة (State) والأمر (Command) معًا عندما ينبغي استعادة
    /// الحالة السابقة للكائن في حال فشل عملية ما.
    ///
    /// ملاحظة: يمكن استخدام UndoManager كبديل.

    func test() {

        let textView = UITextView()
        let undoStack = UndoStack(textView)

        textView.text = "First Change"
        undoStack.save()

        textView.text = "Second Change"
        undoStack.save()

        textView.text = (textView.text ?? "") + " & Third Change"
        textView.textColor = .red
        undoStack.save()

        print(undoStack)

        print("Client: Perform Undo operation 2 times\n")
        undoStack.undo()
        undoStack.undo()

        print(undoStack)
    }
}

class UndoStack: CustomStringConvertible {

    private lazy var mementos = [Memento]()
    private let textView: UITextView

    init(_ textView: UITextView) {
        self.textView = textView
    }

    func save() {
        mementos.append(textView.memento)
    }

    func undo() {
        guard !mementos.isEmpty else { return }
        textView.restore(with: mementos.removeLast())
    }

    var description: String {
        return mementos.reduce("", { $0 + $1.description })
    }
}

protocol Memento: CustomStringConvertible {

    var text: String { get }
    var date: Date { get }
}

extension UITextView {

    var memento: Memento {
        return TextViewMemento(text: text,
                               textColor: textColor,
                               selectedRange: selectedRange)
    }

    func restore(with memento: Memento) {
        guard let textViewMemento = memento as? TextViewMemento else { return }

        text = textViewMemento.text
        textColor = textViewMemento.textColor
        selectedRange = textViewMemento.selectedRange
    }

    struct TextViewMemento: Memento {

        let text: String
        let date = Date()

        let textColor: UIColor?
        let selectedRange: NSRange

        var description: String {
            let time = Calendar.current.dateComponents([.hour, .minute, .second, .nanosecond],
                                                       from: date)
            let color = String(describing: textColor)
            return "Text: \(text)\n" + "Date: \(time.description)\n"
                + "Color: \(color)\n" + "Range: \(selectedRange)\n\n"
        }
    }
}

Text: First Change
Date: hour: 12 minute: 21 second: 50 nanosecond: 821737051 isLeapMonth: false
Color: nil
Range: {12, 0}

Text: Second Change
Date: hour: 12 minute: 21 second: 50 nanosecond: 826483011 isLeapMonth: false
Color: nil
Range: {13, 0}

Text: Second Change & Third Change
Date: hour: 12 minute: 21 second: 50 nanosecond: 829187035 isLeapMonth: false
Color: Optional(UIExtendedSRGBColorSpace 1 0 0 1)
Range: {28, 0}


Client: Perform Undo operation 2 times

Text: First Change
Date: hour: 12 minute: 21 second: 50 nanosecond: 821737051 isLeapMonth: false
Color: nil
Range: {12, 0}
```

### typescript

```typescript
/**
 * يحتفظ المُنشئ ببعض الحالة المهمّة التي قد تتغيّر بمرور الوقت. كما يعرّف دالةً لحفظ
 * الحالة داخل تذكار ودالةً أخرى لاستعادة الحالة منه.
 */
class Originator {
    /**
     * من أجل التبسيط، تُخزَّن حالة المُنشئ داخل متغيّر واحد.
     */
    private state: string;

    constructor(state: string) {
        this.state = state;
        console.log(`Originator: My initial state is: ${state}`);
    }

    /**
     * قد يؤثّر منطق الأعمال الخاص بالمُنشئ في حالته الداخلية. ولذلك ينبغي على العميل أخذ
     * نسخة احتياطية من الحالة قبل استدعاء دوالّ منطق الأعمال عبر الدالة save().
     */
    public doSomething(): void {
        console.log('Originator: I\'m doing something important.');
        this.state = this.generateRandomString(30);
        console.log(`Originator: and my state has changed to: ${this.state}`);
    }

    private generateRandomString(length: number = 10): string {
        const charSet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

        return Array
            .apply(null, { length })
            .map(() => charSet.charAt(Math.floor(Math.random() * charSet.length)))
            .join('');
    }

    /**
     * يحفظ الحالة الحالية داخل تذكار.
     */
    public save(): Memento {
        return new ConcreteMemento(this.state);
    }

    /**
     * يستعيد حالة المُنشئ من كائن تذكار.
     */
    public restore(memento: Memento): void {
        this.state = memento.getState();
        console.log(`Originator: My state has changed to: ${this.state}`);
    }
}

/**
 * توفّر واجهة التذكار طريقةً لاسترجاع البيانات الوصفية للتذكار، مثل تاريخ الإنشاء أو
 * الاسم. غير أنها لا تكشف حالة المُنشئ.
 */
interface Memento {
    getState(): string;

    getName(): string;

    getDate(): string;
}

/**
 * يحتوي التذكار المحسوس (Concrete Memento) على البنية اللازمة لتخزين حالة المُنشئ.
 */
class ConcreteMemento implements Memento {
    private state: string;

    private date: string;

    constructor(state: string) {
        this.state = state;
        this.date = new Date().toISOString().slice(0, 19).replace('T', ' ');
    }

    /**
     * يستخدم المُنشئ هذه الدالة عند استعادة حالته.
     */
    public getState(): string {
        return this.state;
    }

    /**
     * تُستخدم بقية الدوالّ من قِبَل الوصيّ لعرض البيانات الوصفية.
     */
    public getName(): string {
        return `${this.date} / (${this.state.substr(0, 9)}...)`;
    }

    public getDate(): string {
        return this.date;
    }
}

/**
 * لا يعتمد الوصيّ على فئة التذكار المحسوس. ولذلك لا يملك وصولًا إلى حالة المُنشئ المخزّنة
 * داخل التذكار. وهو يتعامل مع جميع التذكارات عبر واجهة التذكار الأساسية.
 */
class Caretaker {
    private mementos: Memento[] = [];

    private originator: Originator;

    constructor(originator: Originator) {
        this.originator = originator;
    }

    public backup(): void {
        console.log('\nCaretaker: Saving Originator\'s state...');
        this.mementos.push(this.originator.save());
    }

    public undo(): void {
        if (!this.mementos.length) {
            return;
        }
        const memento = this.mementos.pop();

        console.log(`Caretaker: Restoring state to: ${memento.getName()}`);
        this.originator.restore(memento);
    }

    public showHistory(): void {
        console.log('Caretaker: Here\'s the list of mementos:');
        for (const memento of this.mementos) {
            console.log(memento.getName());
        }
    }
}

/**
 * كود العميل.
 */
const originator = new Originator('Super-duper-super-puper-super.');
const caretaker = new Caretaker(originator);

caretaker.backup();
originator.doSomething();

caretaker.backup();
originator.doSomething();

caretaker.backup();
originator.doSomething();

console.log('');
caretaker.showHistory();

console.log('\nClient: Now, let\'s rollback!\n');
caretaker.undo();

console.log('\nClient: Once more!\n');
caretaker.undo();

Originator: My initial state is: Super-duper-super-puper-super.

Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: qXqxgTcLSCeLYdcgElOghOFhPGfMxo

Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: iaVCJVryJwWwbipieensfodeMSWvUY

Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: oSUxsOCiZEnohBMQEjwnPWJLGnwGmy

Caretaker: Here's the list of mementos:
2019-02-17 15:14:05 / (Super-dup...)
2019-02-17 15:14:05 / (qXqxgTcLS...)
2019-02-17 15:14:05 / (iaVCJVryJ...)

Client: Now, let's rollback!

Caretaker: Restoring state to: 2019-02-17 15:14:05 / (iaVCJVryJ...)
Originator: My state has changed to: iaVCJVryJwWwbipieensfodeMSWvUY

Client: Once more!

Caretaker: Restoring state to: 2019-02-17 15:14:05 / (qXqxgTcLS...)
Originator: My state has changed to: qXqxgTcLSCeLYdcgElOghOFhPGfMxo
```

