Приведи пример реализации сохранения и восстановления состояния объекта (паттерн Memento).

Ответ

Паттерн Memento позволяет сохранять и восстанавливать внутреннее состояние объекта, не нарушая его инкапсуляцию. Это основа для реализации функциональности Undo/Redo, снапшотов или контрольных точек.

Классическая реализация на C#:

// 1. Хранитель (Memento) — иммутабельный объект, хранящий снимок состояния.
public class TextMemento
{
    public string Content { get; }
    public DateTime CreatedAt { get; }

    public TextMemento(string content)
    {
        Content = content;
        CreatedAt = DateTime.UtcNow;
    }
}

// 2. Создатель (Originator) — объект, состояние которого нужно сохранять.
public class TextDocument
{
    private string _content;

    public string Content
    {
        get => _content;
        set
        {
            Console.WriteLine($"Document content changed to: {value}");
            _content = value;
        }
    }

    // Создать снимок текущего состояния
    public TextMemento CreateMemento()
    {
        return new TextMemento(_content);
    }

    // Восстановить состояние из снимка
    public void RestoreMemento(TextMemento memento)
    {
        _content = memento.Content;
        Console.WriteLine($"Document restored to state from {memento.CreatedAt}");
    }
}

// 3. Опекун (Caretaker) — управляет историей снимков.
public class HistoryTracker
{
    private readonly Stack<TextMemento> _history = new Stack<TextMemento>();

    public void SaveState(TextDocument document)
    {
        _history.Push(document.CreateMemento());
    }

    public void Undo(TextDocument document)
    {
        if (_history.Count > 0)
        {
            document.RestoreMemento(_history.Pop());
        }
    }
}

// Пример использования
class Program
{
    static void Main()
    {
        var doc = new TextDocument();
        var history = new HistoryTracker();

        doc.Content = "Version 1";
        history.SaveState(doc); // Сохраняем

        doc.Content = "Version 2";
        history.SaveState(doc); // Сохраняем

        doc.Content = "Version 3";
        Console.WriteLine($"Current: {doc.Content}"); // Version 3

        history.Undo(doc); // Откат к Version 2
        Console.WriteLine($"After undo: {doc.Content}"); // Version 2

        history.Undo(doc); // Откат к Version 1
        Console.WriteLine($"After second undo: {doc.Content}"); // Version 1
    }
}

Почему это важно:

  • Инкапсуляция: Объект TextDocument сам создаёт и читает снимки, его внутреннее представление состояния (_content) остаётся приватным.
  • Гибкость: Опекун (HistoryTracker) может хранить историю в стеке (для Undo), очереди или любом другом хранилище, не влияя на создателя.
  • Расширяемость: В снимок можно добавлять метаданные (например, timestamp), а также реализовать сжатие или сериализацию состояния для сохранения на диск.

Ответ 18+ 🔞

Давай разберём этот ваш Memento, а то смотрю — народ путается, как хуй с пальмой. Паттерн, который позволяет откатываться назад, как будто ты не наговорил лишнего в пьяном угаре.

Смотри, суть в чём: есть у тебя объект, который меняется, как погода в Питере. И ты хочешь иногда сохранять его состояние, чтобы потом, если накосячил, откатиться. Но делать это надо красиво, не лезя в его кишки напрямую. Вот Memento — это как фотка на память, только для состояния объекта.

Классика жанра на C#:

// 1. Снимок (Memento) — этакая капсула времени, которую нельзя менять.
public class TextMemento
{
    public string Content { get; }
    public DateTime CreatedAt { get; }

    public TextMemento(string content)
    {
        Content = content;
        CreatedAt = DateTime.UtcNow;
    }
}

// 2. Источник (Originator) — тот самый объект, который корежит своё состояние.
public class TextDocument
{
    private string _content;

    public string Content
    {
        get => _content;
        set
        {
            Console.WriteLine($"Документ поменялся на: {value}");
            _content = value;
        }
    }

    // Сделать слепок текущего состояния
    public TextMemento CreateMemento()
    {
        return new TextMemento(_content);
    }

    // Откатиться по слепку, как будто ничего и не было
    public void RestoreMemento(TextMemento memento)
    {
        _content = memento.Content;
        Console.WriteLine($"Документ откатился к состоянию от {memento.CreatedAt}");
    }
}

// 3. Смотритель (Caretaker) — хранитель истории, который всё помнит.
public class HistoryTracker
{
    private readonly Stack<TextMemento> _history = new Stack<TextMemento>();

    public void SaveState(TextDocument document)
    {
        _history.Push(document.CreateMemento());
    }

    public void Undo(TextDocument document)
    {
        if (_history.Count > 0)
        {
            document.RestoreMemento(_history.Pop());
        }
    }
}

// Как это всё в деле работает
class Program
{
    static void Main()
    {
        var doc = new TextDocument();
        var history = new HistoryTracker();

        doc.Content = "Версия 1";
        history.SaveState(doc); // Сохранили, можно спать спокойно

        doc.Content = "Версия 2";
        history.SaveState(doc); // Ещё один слепок, на всякий пожарный

        doc.Content = "Версия 3";
        Console.WriteLine($"Сейчас: {doc.Content}"); // Версия 3

        history.Undo(doc); // Ой, бля, не то написал — откат к Версии 2
        Console.WriteLine($"После отката: {doc.Content}"); // Версия 2

        history.Undo(doc); // Да и это хуйня, давай обратно к Версии 1
        Console.WriteLine($"После второго отката: {doc.Content}"); // Версия 1
    }
}

А теперь, почему это не просто прихоть, а полезная хуйня:

  • Инкапсуляция на высоте: Сам TextDocument и создаёт, и читает свои снимки. Никакой посторонний урод не лезет в его приватное поле _content. Всё чинно, благородно.
  • Гибкость пиздец: Смотритель (HistoryTracker) может хранить историю как угодно — в стеке (для Undo), в очереди, в базе, на облаке. Источнику (TextDocument) похуй, он просто делает снимки.
  • Расширяемость: Хочешь — добавляй в снимок метаданные (кто сохранил, комментарий). Хочешь — сжимай состояние или сейвь на диск. Всё в твоих руках, главное — не обосраться.

Короче, если нужно реализовать Ctrl+Z в своём приложении, или снапшоты системы, или просто возможность отката — Memento твой бро. Только не забывай, что если состояние объекта весит как чугунный мост, то и снимки будут такими же — можно память проебать в момент.