سی ام اس نویسسی ام اس نویسیاد بگیر، سیستم خودتو بنویس
مجلهتماس با ما
سی ام اس نویسسی ام اس نویس

CMS نویس مرجعی برای آموزش برنامه نویسی وب و PHP، توسعه افزونه‌های اختصاصی وردپرس، طراحی ورک‌فلو و اتوماسیون با n8n و توسعه CMS های اختصاصی است. هدف ما ارائه آموزش‌های کاربردی و راهکارهای حرفه‌ای برای توسعه‌دهندگان، کسب‌وکارها و علاقه‌مندان به دنیای برنامه‌نویسی است.

پیوندهای سریع
محصولات فیزیکیمحصولات دیجیتالدوره‌های آموزشی
سی ام اس نویس
مجله سی ام اس نویستماس با ماپنل کاربری
سی ام اس نویس — تمامی حقوق برای سی ام اس نویس محفوظ است. © 1406 تماس با ما09307711941
Memento Pattern در PHP چیست؟ آموزش الگوی Memento با مثال عملی

Memento Pattern در PHP چیست؟ آموزش الگوی Memento با مثال عملی

ممدیر کل سیستم ۲۲ شهریور ۱۴۰۵ ۱۸ دقیقه مطالعه ۲ بازدید
Memento Pattern در PHP چیست؟ آموزش الگوی Memento با مثال عملی

در بسیاری از نرم‌افزارها لازم است وضعیت قبلی یک Object را ذخیره کنیم تا در صورت نیاز بتوانیم آن را به حالت قبل برگردانیم. قابلیت‌هایی مانند Undo در ویرایشگر متن، بازگردانی تنظیمات، تاریخچه تغییرات، ذخیره پیش‌نویس و Rollback نمونه‌هایی از این نیاز هستند.

اگر وضعیت داخلی یک Object را مستقیماً در اختیار سایر بخش‌های برنامه قرار دهیم، وابستگی زیادی بین کلاس‌ها ایجاد می‌شود و کنترل روی اطلاعات داخلی Object کاهش پیدا می‌کند. Memento Pattern برای حل همین مسئله استفاده می‌شود.

در این مقاله با مفهوم Memento Pattern در PHP، اجزای اصلی آن، نحوه پیاده‌سازی، قابلیت Undo و کاربرد این الگو در پروژه‌های Laravel آشنا می‌شویم.

Memento Pattern چیست؟

Memento Pattern یا الگوی یادگاری، یکی از الگوهای رفتاری یا Behavioral Design Pattern است که امکان ذخیره‌کردن وضعیت فعلی یک Object و بازیابی آن در آینده را فراهم می‌کند.

هدف اصلی این الگو این است که بتوانیم وضعیت قبلی یک Object را بدون افشای جزئیات داخلی آن ذخیره کنیم. به زبان ساده، Memento یک Snapshot از وضعیت Object می‌سازد و در زمان نیاز همان وضعیت را به Object اصلی بازمی‌گرداند.

طبق تعریف رایج این الگو، Memento از سه بخش اصلی تشکیل می‌شود:

  1. Originator
  2. Memento
  3. Caretaker

این ساختار باعث می‌شود Object اصلی مسئول مدیریت وضعیت خودش باشد و کلاس دیگری فقط وظیفه نگهداری تاریخچه یا Snapshotها را بر عهده بگیرد. <Cite refs={["turn0search1","turn0search2"]} />

یک مثال واقعی از Memento Pattern

فرض کنید در سایت خود یک ویرایشگر متن دارید. کاربر متن زیر را وارد می‌کند:


Hello

سپس متن را به شکل زیر تغییر می‌دهد:


Hello World

بعد از آن متن را به شکل زیر تغییر می‌دهد:


Hello World From PHP

اگر کاربر روی دکمه Undo کلیک کند، انتظار دارد متن به وضعیت قبلی برگردد:


Hello World

با کلیک دوباره روی Undo، متن باید به این مقدار برگردد:


Hello

برای پیاده‌سازی چنین قابلیتی می‌توان قبل از هر تغییر، وضعیت فعلی Editor را ذخیره کرد. هر وضعیت ذخیره‌شده یک Memento خواهد بود.

اجزای اصلی Memento Pattern

۱. Originator

Originator همان Object اصلی است که وضعیت آن تغییر می‌کند. این کلاس باید بتواند:

  • وضعیت فعلی خودش را ذخیره کند.
  • یک Memento ایجاد کند.
  • وضعیت خودش را از روی یک Memento بازیابی کند.

در مثال ما، کلاس TextEditor نقش Originator را دارد.

۲. Memento

Memento یک Snapshot از وضعیت Object است. این Snapshot باید اطلاعات لازم برای بازیابی وضعیت قبلی را نگهداری کند.

در مثال ویرایشگر متن، وضعیت ذخیره‌شده می‌تواند شامل موارد زیر باشد:

  • محتوای متن
  • موقعیت Cursor
  • وضعیت انتخاب متن
  • تنظیمات مربوط به ویرایشگر

بهتر است Memento بعد از ساخته‌شدن تغییرناپذیر باشد؛ یعنی سایر کلاس‌ها نتوانند اطلاعات داخل آن را به‌صورت مستقیم تغییر دهند.

۳. Caretaker

Caretaker مسئول نگهداری تاریخچه Mementoها است. این کلاس معمولاً تصمیم می‌گیرد چه زمانی Snapshot ذخیره شود و چه زمانی Snapshot قبلی به Originator بازگردانده شود.

Caretaker نباید از جزئیات داخلی Originator اطلاع داشته باشد. وظیفه آن فقط نگهداری و مدیریت Mementoها است.

چرا نباید وضعیت Object را مستقیماً در اختیار دیگر کلاس‌ها قرار دهیم؟

فرض کنید کلاس Editor دارای Property عمومی زیر باشد:


public array $state = [];

در این حالت هر کلاس دیگری می‌تواند وضعیت Editor را تغییر دهد:


$editor->state['content'] = 'New content';

این روش چند مشکل دارد:

  • Encapsulation از بین می‌رود.
  • سایر کلاس‌ها به ساختار داخلی Editor وابسته می‌شوند.
  • تغییر ساختار State می‌تواند بخش‌های دیگر برنامه را خراب کند.
  • کنترل اعتبارسنجی وضعیت دشوار می‌شود.
  • تست‌کردن کد پیچیده‌تر خواهد شد.

در Memento Pattern، وضعیت توسط خود Originator ساخته و بازیابی می‌شود و Caretaker فقط Snapshot را نگهداری می‌کند.

پیاده‌سازی Memento Pattern در PHP

در این مثال یک ویرایشگر متن ساده ایجاد می‌کنیم که قابلیت‌های زیر را دارد:

  • نوشتن متن
  • ذخیره وضعیت فعلی
  • بازیابی وضعیت قبلی
  • نمایش محتوای فعلی
  • Undo با استفاده از تاریخچه

مرحله اول: ساخت کلاس Memento

ابتدا کلاس EditorMemento را ایجاد می‌کنیم:


<?php



declare(strict_types=1);



final class EditorMemento

{

    public function __construct(

        private readonly string $content,

        private readonly int $cursorPosition,

    ) {

    }



    public function content(): string

    {

        return $this->content;

    }



    public function cursorPosition(): int

    {

        return $this->cursorPosition;

    }

}

در این کلاس دو مقدار ذخیره می‌شود:

  • content: محتوای ویرایشگر
  • cursorPosition: موقعیت Cursor

استفاده از private readonly باعث می‌شود مقادیر بعد از ساخته‌شدن Object قابل تغییر نباشند.

این موضوع اهمیت زیادی دارد؛ زیرا Memento باید Snapshot ثابتی از وضعیت قبلی باشد.

مرحله دوم: ساخت Originator

اکنون کلاس TextEditor را ایجاد می‌کنیم:


<?php



declare(strict_types=1);



final class TextEditor

{

    private string $content = '';



    private int $cursorPosition = 0;



    public function write(string $text): void

    {

        $this->content .= $text;

        $this->cursorPosition = strlen($this->content);

    }



    public function save(): EditorMemento

    {

        return new EditorMemento(

            content: $this->content,

            cursorPosition: $this->cursorPosition,

        );

    }



    public function restore(EditorMemento $memento): void

    {

        $this->content = $memento->content();

        $this->cursorPosition = $memento->cursorPosition();

    }



    public function content(): string

    {

        return $this->content;

    }



    public function cursorPosition(): int

    {

        return $this->cursorPosition;

    }

}

در این کلاس:

متد write

این متد متن جدید را به محتوای فعلی اضافه می‌کند.

متد save

این متد وضعیت فعلی Editor را به یک Memento تبدیل می‌کند.

متد restore

این متد وضعیت داخلی Editor را از روی Memento بازیابی می‌کند.

متد content

محتوای فعلی Editor را برمی‌گرداند.

نکته مهم این است که کلاس EditorMemento به جز متدهای خواندن، امکان تغییر وضعیت را در اختیار سایر بخش‌های برنامه قرار نمی‌دهد.

مرحله سوم: ساخت Caretaker

اکنون باید کلاسی داشته باشیم که Mementoها را در یک تاریخچه نگهداری کند:


<?php



declare(strict_types=1);



final class EditorHistory

{

    /**

     * @var list<EditorMemento>

     */

    private array $history = [];



    public function push(EditorMemento $memento): void

    {

        $this->history[] = $memento;

    }



    public function pop(): ?EditorMemento

    {

        if ($this->history === []) {

            return null;

        }



        return array_pop($this->history);

    }



    public function count(): int

    {

        return count($this->history);

    }

}

متد push یک Snapshot جدید را به تاریخچه اضافه می‌کند.

متد pop آخرین Snapshot را از تاریخچه خارج می‌کند و به برنامه برمی‌گرداند.

اگر تاریخچه خالی باشد، مقدار null برگردانده می‌شود.

اجرای کامل مثال

اکنون می‌توانیم سه کلاس ساخته‌شده را در یک فایل استفاده کنیم:


<?php



declare(strict_types=1);



$editor = new TextEditor();

$history = new EditorHistory();



// ذخیره وضعیت اولیه

$history->push($editor->save());



// نوشتن متن اول

$editor->write('Hello ');

$history->push($editor->save());



// نوشتن متن دوم

$editor->write('World');



echo $editor->content() . PHP_EOL;

// Hello World



// بازگردانی به وضعیت قبلی

$previousState = $history->pop();



if ($previousState !== null) {

    $editor->restore($previousState);

}



echo $editor->content() . PHP_EOL;

// Hello



// بازگردانی به وضعیت اولیه

$previousState = $history->pop();



if ($previousState !== null) {

    $editor->restore($previousState);

}



echo $editor->content() . PHP_EOL;

// خالی

در این مثال، قبل از هر تغییر یک Snapshot ذخیره شده است. زمانی که کاربر Undo را اجرا می‌کند، آخرین Snapshot از تاریخچه برداشته شده و به Editor بازگردانده می‌شود.

نحوه اجرای کد

فرض کنید کد بالا در فایل زیر قرار دارد:


memento-example.php

برای اجرای آن در ترمینال از دستور زیر استفاده کنید:


php memento-example.php

خروجی مورد انتظار:


Hello World

Hello

پیاده‌سازی Undo و Redo

در یک ویرایشگر واقعی، فقط Undo کافی نیست. معمولاً کاربر باید بتواند بعد از Undo، دوباره با استفاده از Redo به وضعیت جلوتر برگردد.

برای این کار به دو Stack نیاز داریم:

  • Undo Stack
  • Redo Stack

منطق کلی به شکل زیر است:

  1. قبل از هر تغییر، وضعیت فعلی در Undo Stack ذخیره می‌شود.
  2. هنگام انجام تغییر جدید، Redo Stack خالی می‌شود.
  3. هنگام Undo، وضعیت فعلی در Redo Stack ذخیره می‌شود.
  4. آخرین وضعیت از Undo Stack بازیابی می‌شود.
  5. هنگام Redo، وضعیت فعلی در Undo Stack ذخیره می‌شود.
  6. آخرین وضعیت از Redo Stack بازیابی می‌شود.

نمونه ساده از History با پشتیبانی از Undo و Redo:


<?php



declare(strict_types=1);



final class EditorHistoryWithRedo

{

    /**

     * @var list<EditorMemento>

     */

    private array $undoStack = [];



    /**

     * @var list<EditorMemento>

     */

    private array $redoStack = [];



    public function save(EditorMemento $memento): void

    {

        $this->undoStack[] = $memento;

        $this->redoStack = [];

    }



    public function undo(): ?EditorMemento

    {

        if ($this->undoStack === []) {

            return null;

        }



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



        if ($memento !== null) {

            $this->redoStack[] = $memento;

        }



        return $this->undoStack === []

            ? null

            : $this->undoStack[array_key_last($this->undoStack)];

    }



    public function redo(): ?EditorMemento

    {

        if ($this->redoStack === []) {

            return null;

        }



        return array_pop($this->redoStack);

    }

}

این نمونه برای نمایش مفهوم دو Stack است. در پروژه واقعی بهتر است هنگام Undo و Redo، وضعیت فعلی نیز به‌صورت دقیق مدیریت شود تا ترتیب Snapshotها از بین نرود.

نسخه بهتر برای مدیریت Undo و Redo

برای مدیریت کامل‌تر، می‌توان یک History Manager ساخت که خودش Editor را بشناسد:


<?php



declare(strict_types=1);



final class EditorHistoryManager

{

    /**

     * @var list<EditorMemento>

     */

    private array $undoStack = [];



    /**

     * @var list<EditorMemento>

     */

    private array $redoStack = [];



    public function __construct(

        private readonly TextEditor $editor,

    ) {

    }



    public function backup(): void

    {

        $this->undoStack[] = $this->editor->save();

        $this->redoStack = [];

    }



    public function undo(): void

    {

        if ($this->undoStack === []) {

            return;

        }



        $this->redoStack[] = $this->editor->save();



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



        if ($memento !== null) {

            $this->editor->restore($memento);

        }

    }



    public function redo(): void

    {

        if ($this->redoStack === []) {

            return;

        }



        $this->undoStack[] = $this->editor->save();



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



        if ($memento !== null) {

            $this->editor->restore($memento);

        }

    }

}

استفاده از این کلاس:


<?php



$editor = new TextEditor();



$history = new EditorHistoryManager($editor);



$history->backup();

$editor->write('Hello');



$history->backup();

$editor->write(' World');



echo $editor->content() . PHP_EOL;

// Hello World



$history->undo();



echo $editor->content() . PHP_EOL;

// Hello



$history->redo();



echo $editor->content() . PHP_EOL;

// Hello World

در پروژه‌های واقعی باید مشخص کنید که Snapshot قبل از تغییر ذخیره شود یا بعد از تغییر. مهم‌ترین موضوع این است که این تصمیم در تمام بخش‌های برنامه یکسان باشد.

کاربردهای Memento Pattern در پروژه‌های PHP

Memento Pattern فقط برای ویرایشگر متن استفاده نمی‌شود. این الگو در موقعیت‌های مختلفی کاربرد دارد.

۱. ذخیره پیش‌نویس مقاله

در یک CMS ممکن است نویسنده چندین بار محتوای مقاله را تغییر دهد. با ذخیره Snapshot می‌توان نسخه‌های قبلی مقاله را نگهداری کرد.

۲. بازگردانی تنظیمات

در پنل مدیریت سایت، کاربر ممکن است تنظیمات قالب، سئو یا سیستم را تغییر دهد. اگر قبل از تغییر یک Memento ساخته شود، می‌توان تنظیمات را به حالت قبلی بازگرداند.

۳. تاریخچه تغییرات فرم

در فرم‌های چندمرحله‌ای می‌توان وضعیت فرم را در هر مرحله ذخیره کرد تا کاربر بتواند به مراحل قبلی بازگردد.

۴. ویرایشگر گرافیکی

در نرم‌افزارهای طراحی، تغییرات مربوط به جابه‌جایی، حذف یا ویرایش عناصر می‌تواند به‌صورت Snapshot ذخیره شود.

۵. وضعیت موقت یک مدل

در برخی فرآیندهای پیچیده می‌توان وضعیت فعلی یک Domain Object را ذخیره کرد و در صورت ناموفق‌بودن عملیات، آن را بازیابی کرد.

استفاده از Memento Pattern در Laravel

در Laravel نیز می‌توان از این الگو برای مدیریت وضعیت موقت یا تاریخچه تغییرات استفاده کرد.

برای مثال فرض کنید در پنل مدیریت، کاربر در حال ویرایش یک محصول است. قبل از اعمال تغییرات می‌توان وضعیت فعلی محصول را به‌صورت Snapshot ذخیره کرد.

یک Memento ساده برای محصول:


<?php



declare(strict_types=1);



final class ProductMemento

{

    public function __construct(

        public readonly string $name,

        public readonly int $price,

        public readonly int $stock,

    ) {

    }

}

کلاس Originator:


<?php



declare(strict_types=1);



final class ProductEditor

{

    public function __construct(

        private string $name,

        private int $price,

        private int $stock,

    ) {

    }



    public function save(): ProductMemento

    {

        return new ProductMemento(

            name: $this->name,

            price: $this->price,

            stock: $this->stock,

        );

    }



    public function restore(ProductMemento $memento): void

    {

        $this->name = $memento->name;

        $this->price = $memento->price;

        $this->stock = $memento->stock;

    }



    public function update(

        string $name,

        int $price,

        int $stock,

    ): void {

        $this->name = $name;

        $this->price = $price;

        $this->stock = $stock;

    }

}

در Laravel می‌توان از چنین ساختاری برای موارد زیر استفاده کرد:

  • تاریخچه تغییرات تنظیمات
  • مدیریت پیش‌نویس‌ها
  • بازگردانی وضعیت فرم
  • ذخیره نسخه‌های موقت Domain Object
  • پیاده‌سازی Undo در پنل مدیریت
  • ذخیره وضعیت قبل از یک فرآیند پیچیده

البته Memento جایگزین تراکنش دیتابیس، Backup واقعی یا سیستم Versioning دائمی نیست. اگر لازم است اطلاعات بعد از Restart سرور یا خرابی سیستم باقی بمانند، بهتر است Snapshotها در دیتابیس یا فضای ذخیره‌سازی دائمی ذخیره شوند.

تفاوت Memento Pattern و Command Pattern

Command Pattern یک عملیات یا درخواست را به‌صورت یک Object ذخیره می‌کند؛ اما Memento Pattern وضعیت یک Object را ذخیره می‌کند.

Memento PatternCommand Pattern
وضعیت Object را ذخیره می‌کندعملیات را ذخیره می‌کند
برای Snapshot و Rollback مناسب استبرای صف، Undo و اجرای عملیات مناسب است
روی State تمرکز داردروی Action تمرکز دارد
معمولاً وضعیت قبلی را نگهداری می‌کندمی‌تواند عملیات معکوس داشته باشد

این دو الگو می‌توانند با هم استفاده شوند. برای مثال یک Command مربوط به ویرایش محصول می‌تواند قبل از اجرا، وضعیت قبلی محصول را با Memento ذخیره کند.

تفاوت Memento Pattern و State Pattern

هر دو الگو با وضعیت Object سروکار دارند، اما هدف آن‌ها متفاوت است.

در State Pattern، رفتار Object با توجه به وضعیت فعلی تغییر می‌کند. برای مثال یک سفارش در حالت‌های ایجادشده، پرداخت‌شده، ارسال‌شده یا لغوشده رفتار متفاوتی دارد.

در Memento Pattern، هدف ذخیره و بازیابی وضعیت قبلی Object است.

به‌صورت خلاصه:

  • State برای تغییر رفتار بر اساس وضعیت استفاده می‌شود.
  • Memento برای ذخیره و بازیابی وضعیت استفاده می‌شود.

تفاوت Memento Pattern و Backup دیتابیس

Memento معمولاً برای ذخیره وضعیت یک Object یا یک بخش محدود از برنامه استفاده می‌شود. این Snapshot ممکن است فقط در حافظه نگهداری شود.

اما Backup دیتابیس برای محافظت از اطلاعات پایدار و گسترده سیستم استفاده می‌شود.

Memento معمولاً مناسب موارد زیر است:

  • Undo
  • پیش‌نویس
  • تغییرات موقت
  • تاریخچه کوتاه‌مدت
  • Rollback یک Object

Backup دیتابیس مناسب موارد زیر است:

  • بازیابی اطلاعات بعد از خرابی
  • مهاجرت سرور
  • بازگردانی کل دیتابیس
  • حفظ اطلاعات در بلندمدت

بنابراین نباید Memento را جایگزین Backup یا Transaction دیتابیس در نظر گرفت.

مزایای Memento Pattern

حفظ Encapsulation

کلاس‌های دیگر نیازی ندارند به Propertyهای داخلی Originator دسترسی مستقیم داشته باشند.

پیاده‌سازی Undo

با ذخیره Snapshotهای قبلی می‌توان قابلیت Undo را ایجاد کرد.

جداسازی مسئولیت‌ها

Originator مسئول وضعیت خودش است و Caretaker مسئول نگهداری تاریخچه است.

تست‌پذیری بهتر

می‌توان وضعیت Object را ذخیره کرد، یک عملیات را اجرا کرد و سپس با بازیابی Snapshot نتیجه را بررسی کرد.

مناسب برای تاریخچه تغییرات

در برنامه‌هایی که وضعیت Object چندین بار تغییر می‌کند، Memento می‌تواند ساختار مناسبی برای نگهداری نسخه‌های قبلی ایجاد کند.

معایب Memento Pattern

مصرف حافظه

اگر وضعیت Object بزرگ باشد و Snapshotهای زیادی ذخیره شوند، مصرف حافظه افزایش پیدا می‌کند.

پیچیدگی Undo و Redo

مدیریت هم‌زمان Undo و Redo به طراحی دقیق Stackها نیاز دارد.

هزینه ذخیره Snapshot

اگر در هر تغییر کل وضعیت Object ذخیره شود، ممکن است حجم زیادی از اطلاعات تکراری ایجاد شود.

مشکل در Objectهای وابسته

Memento معمولاً وضعیت یک Object را ذخیره می‌کند. اگر Object به چندین منبع یا Object دیگر وابسته باشد، بازیابی فقط یک Snapshot ممکن است همه تغییرات را برنگرداند.

ناسازگاری نسخه‌ها

اگر ساختار Object در طول زمان تغییر کند، Snapshotهای قدیمی ممکن است دیگر با ساختار جدید سازگار نباشند.

چگونه Memento Pattern را بهینه کنیم؟

محدودکردن تعداد Snapshotها

می‌توان فقط ۲۰ یا ۵۰ وضعیت آخر را نگهداری کرد:


private const MAX_HISTORY = 50;

بعد از ذخیره Snapshot جدید، اگر تعداد تاریخچه بیشتر از مقدار مجاز شد، قدیمی‌ترین Snapshot را حذف کنید.

ذخیره فقط هنگام تغییر

نباید در هر بار اجرای متد، Snapshot جدید ایجاد شود. بهتر است فقط زمانی Snapshot ذخیره شود که وضعیت واقعاً تغییر کرده باشد.

استفاده از Diff برای متن‌های بزرگ

در ویرایشگرهای حرفه‌ای، به‌جای ذخیره کل متن در هر مرحله می‌توان فقط تفاوت بین دو نسخه را ذخیره کرد. این روش پیچیده‌تر است اما مصرف حافظه را کاهش می‌دهد.

جداکردن تاریخچه از منطق اصلی

بهتر است مدیریت تاریخچه در یک کلاس جداگانه انجام شود تا Originator بیش از حد بزرگ و پیچیده نشود.

تغییرناپذیرکردن Memento

Memento باید تا حد امکان Immutable باشد تا Snapshot ذخیره‌شده به‌صورت ناخواسته تغییر نکند.

تست Memento Pattern با PHPUnit

برای اطمینان از عملکرد صحیح این الگو، می‌توان تست‌های زیر را ایجاد کرد:

  • وضعیت ذخیره‌شده به‌درستی بازیابی شود.
  • بعد از Undo، متن به مقدار قبلی برگردد.
  • بازیابی Memento خالی باعث خطا نشود.
  • Memento بعد از ساخته‌شدن تغییر نکند.
  • تعداد Snapshotهای تاریخچه درست باشد.

نمونه تست:


<?php



declare(strict_types=1);



use PHPUnit\Framework\TestCase;



final class TextEditorTest extends TestCase

{

    public function testEditorCanRestorePreviousState(): void

    {

        $editor = new TextEditor();



        $editor->write('Hello');



        $memento = $editor->save();



        $editor->write(' World');



        $editor->restore($memento);



        self::assertSame('Hello', $editor->content());

    }

}

این تست بررسی می‌کند که پس از اضافه‌شدن متن جدید، بازیابی Memento بتواند Editor را به وضعیت قبلی برگرداند.

اشتباهات رایج در استفاده از Memento Pattern

عمومی‌کردن تمام Propertyهای داخلی

این کار Encapsulation را از بین می‌برد و باعث وابستگی شدید کلاس‌ها می‌شود.

قابل‌تغییر بودن Memento

اگر سایر کلاس‌ها بتوانند Snapshot را تغییر دهند، دیگر نمی‌توان به صحت تاریخچه اعتماد کرد.

نگهداری تاریخچه در Originator

این روش ممکن است Originator را به کلاس بزرگی تبدیل کند که هم منطق اصلی و هم مدیریت تاریخچه را انجام می‌دهد.

استفاده از Memento برای Backup کامل سیستم

Memento برای ذخیره وضعیت Object طراحی شده است، نه Backup کامل دیتابیس یا فایل‌های سرور.

ذخیره Snapshot بدون محدودیت

در برنامه‌های بزرگ، نگهداری نامحدود Snapshotها می‌تواند باعث افزایش مصرف حافظه یا فضای ذخیره‌سازی شود.

ذخیره Objectهای وابسته بدون توجه به Deep Copy

اگر Snapshot فقط Reference به یک Object دیگر نگهداری کند، تغییر آن Object ممکن است روی وضعیت ذخیره‌شده نیز اثر بگذارد. باید مشخص شود که Snapshot به کپی مستقل نیاز دارد یا خیر.

چه زمانی از Memento Pattern استفاده کنیم؟

استفاده از Memento Pattern زمانی مناسب است که:

  • به قابلیت Undo نیاز دارید.
  • وضعیت یک Object چندین بار تغییر می‌کند.
  • می‌خواهید تاریخچه تغییرات داشته باشید.
  • نباید جزئیات داخلی Object را در اختیار سایر کلاس‌ها قرار دهید.
  • لازم است قبل از یک عملیات، Snapshot ایجاد شود.
  • می‌خواهید وضعیت قبلی یک Domain Object را بازیابی کنید.

چه زمانی از Memento Pattern استفاده نکنیم؟

اگر فقط لازم است یک مقدار ساده را ذخیره کنید، شاید استفاده از Memento بیش از حد پیچیده باشد.

همچنین در شرایط زیر بهتر است راهکار دیگری بررسی شود:

  • Backup کامل دیتابیس
  • تراکنش‌های پیچیده چندجدولی
  • ذخیره دائمی فایل‌های بزرگ
  • سیستم Versioning حرفه‌ای
  • تاریخچه‌ای که باید بین چند سرور به‌صورت توزیع‌شده مدیریت شود

در این شرایط می‌توان از Database Transaction، Event Sourcing، Versioning، فایل Backup یا سیستم ذخیره‌سازی جداگانه استفاده کرد.

جمع‌بندی

Memento Pattern در PHP یک الگوی رفتاری برای ذخیره و بازیابی وضعیت قبلی Object است. این الگو از سه بخش اصلی تشکیل می‌شود:

  • Originator برای مدیریت وضعیت اصلی
  • Memento برای نگهداری Snapshot
  • Caretaker برای مدیریت تاریخچه

با استفاده از این الگو می‌توان قابلیت‌هایی مانند Undo، Redo، ذخیره پیش‌نویس، تاریخچه تغییرات و بازگردانی وضعیت را پیاده‌سازی کرد.

نکته مهم این است که Memento باید تا حد امکان تغییرناپذیر باشد و مسئولیت ذخیره وضعیت از منطق اصلی Object جدا شود. همچنین برای اطلاعات دائمی و Backupهای بزرگ، بهتر است از دیتابیس یا سیستم ذخیره‌سازی مناسب استفاده شود.

سوالات متداول

Memento Pattern در PHP چه کاربردی دارد؟

از Memento Pattern برای ذخیره و بازیابی وضعیت قبلی Object استفاده می‌شود. قابلیت‌هایی مانند Undo، Redo، تاریخچه تغییرات و ذخیره پیش‌نویس از کاربردهای آن هستند.

سه جزء اصلی Memento Pattern چیست؟

سه جزء اصلی این الگو عبارت‌اند از Originator، Memento و Caretaker.

آیا Memento Pattern جایگزین Transaction دیتابیس است؟

خیر. Memento برای Snapshot یک Object استفاده می‌شود و جایگزین Transaction دیتابیس یا Backup کامل سیستم نیست.

تفاوت Memento و Command چیست؟

Memento وضعیت Object را ذخیره می‌کند، اما Command یک عملیات را به‌صورت Object ذخیره می‌کند. این دو الگو می‌توانند برای ساخت Undo در کنار یکدیگر استفاده شوند.

آیا می‌توان از Memento Pattern در Laravel استفاده کرد؟

بله. در Laravel می‌توان از Memento برای مدیریت پیش‌نویس‌ها، تاریخچه تنظیمات، وضعیت فرم‌ها و بازگردانی وضعیت موقت Domain Objectها استفاده کرد.

نظرات (0)

نظرات پس از تأیید مدیر نمایش داده می‌شوند

هنوز نظری ثبت نشده است؛ اولین نفر باشید!

مقالات مرتبط

ساخت CMS با Laravel 12؛ آموزش ساخت سیستم مدیریت محتوا از صفر

ساخت CMS با Laravel 12؛ آموزش ساخت سیستم مدیریت محتوا از صفر

۲۴ دقیقه
ساخت فروشگاه اینترنتی با Laravel 12؛ آموزش پروژه‌محور از صفر تا طراحی هسته فروشگاه

ساخت فروشگاه اینترنتی با Laravel 12؛ آموزش پروژه‌محور از صفر تا طراحی هسته فروشگاه

۲۲ دقیقه
ساخت Marketplace با Laravel؛ آموزش طراحی فروشگاه چندفروشندگی

ساخت Marketplace با Laravel؛ آموزش طراحی فروشگاه چندفروشندگی

۲۲ دقیقه
مدیریت Queueهای سنگین در Laravel؛ آموزش پردازش کارهای طولانی

مدیریت Queueهای سنگین در Laravel؛ آموزش پردازش کارهای طولانی

۲۲ دقیقه