Книга: Погружение в Паттерны Проектирования
Назад: Посредник
Дальше: Наблюдатель
и позволяет хранить резервные копии сложного состояния текстового редактора и, если потребуется, восстанавливать его.

Структура классов примера паттерна Снимок

Пример сохранения снимков состояния текстового редактора.

Объекты команд выступают в роли опекунов и запрашивают снимки у редактора перед тем, как выполнить своё действие. Если потребуется отмена операции, команда сможет восстановить состояние редактора, используя сохранённый снимок.

При этом снимок не имеет публичных полей, поэтому никакой объект не сможет получить доступа его данным. Снимки связаны с определённым редактором, которых их создал и сам восстанавливает его состояние. Это позволяет программе иметь одновременно несколько объектов редакторов, например, разбитых по вкладкам.

// Класс создателя должен иметь специальный метод, который
// сохраняет состояние создателя в новом объекте-снимке.
class Editor is
  private field textcurXcurYselectionWidth

  method setText(text) is
    this.text = text

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

  method setSelectionWidth(width) is
    this.selectionWidth = width

  method createSnapshot(): EditorState is
    // Снимок — неизменяемый объект, поэтому Создатель
    // передаёт все своё состояние через
    // параметры конструктора.
    return new Snapshot(this, text, curX, curY, selectionWidth)

// Снимок хранит прошлое состояние редактора.
class Snapshot is
  private field editor: Editor
  private field textcurXcurYselectionWidth

  constructor Snapshot(editor, text, curX, curY, selectionWidth) is
    this.editor = editor
    this.text = text
    this.curX = curX
    this.curY = curY
    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.saveState()

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

Применимость

Когда вам нужно сохранять мгновенный снимок состояния объекта (или его части), чтобы впоследствии объект можно было восстановить в том же состоянии.

Паттерн Снимок позволяет делать любое количество снимков объекта и хранить их независимо от объекта, с которого делают снимок. Снимки часто используют не только для реализации операции отмены, но и для транзакций, когда состояние объекта нужно откатить, если операция не удалась.

Когда прямое получение состояния объекта раскрывает детали его реализации и нарушает инкапсуляцию.

Паттерн предлагает изготовить снимок самому исходному объекту, так как ему доступны все поля, даже приватные.

Шаги реализации

  1. Определите класс создателя, объекты которого должны создавать снимки своего состояния.

  2. Создайте класс снимка и опишите в нём все те же поля, которые имеются в оригинальном классе-создателе.

  3. Сделайте объекты снимков неизменяемыми. Они должны получать начальные значения только один раз, через свой конструктор.

  4. Если ваш язык программирования это позволяет, сделайте класс снимка вложенным в класс создателя.

    Если нет, извлеките из класса снимка пустой интерфейс, который будет доступен остальным объектам программы. Впоследствии вы можете добавить некоторые вспомогательные методы в этот интерфейс, дающие доступ к метаданным снимка, однако прямой доступ к данным создателя должен быть исключён.

  5. Добавьте в класс создателя метод получения снимков. Создатель должен создавать новые объекты снимков, передавая значения своих полей через конструктор.

    Сигнатура метода должна возвращать снимки через ограниченный интерфейс, если он у вас есть. Сам класс должен работать с конкретным классом снимка.

  6. Добавьте в класс создателя метод восстановления из снимка. Что касается привязки к типам, руководствуйтесь той же логикой, что и в пункте 4.

  7. Опекуны, будь то история операций, объекты команд или нечто иное, должны знать о том, когда запрашивать снимки у создателя, где их хранить, и когда восстанавливать.

  8. Связь опекунов с создателями можно перенести внутрь снимков. В этом случае каждый снимок будет привязан к своему создателю и должен будет сам восстанавливать его состояние. Но это будет работать либо если классы снимков вложены в классы создателей, либо если создатели имеют сеттеры для установки значений своих полей.

Преимущества и недостатки

Отношения с другими паттернами

Назад: Посредник
Дальше: Наблюдатель

asd
asdda