Ответ
Паттерн 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 твой бро. Только не забывай, что если состояние объекта весит как чугунный мост, то и снимки будут такими же — можно память проебать в момент.