Javenue logo

Javenue

Программирование на Java

Информационные технологии

Паттерн Memento в Java - Память

Память относится к поведенческим паттернам проектирования (behavioral pattern). Он нужен для хранения определенного состояния объекта. Одно из условий реализации паттерна - при сохранении состояния не должна нарушаться парадигма ООП инкапсуляция. Ну и самое главное - должна присутствовать возможность возврата к предыдущему состоянию объекта.

Классическим примером реализации этого паттерна считается генератор псевдослучайных чисел. Действительно, если проинициализировать генератор одним и тем же seed'ом, то получим аналогичную последовательнось чисел.

Ну, довольно сухих слов. Перейдем к практическому примеру паттерна Memento на Java.

Пример реализации Memento

Memento может понадобиться, например, при создании preview для некоторой формы.

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

public class SiteDescrBean {
    private long id;
    private String name;
    private String theURL;

    private String description;
    private int category;

    /* setters and getters
       are really boring methods :) */

    private Memento undo;

    private class Memento {
        String siteDescription;
        int siteCategory;

        Memento() {
            siteDescription = description;
            siteCategory = category;
        }

        String getDescr() {
            return siteDescription;
        }

        int getCateg() {
            return siteCategory;
        }
    }

    public void preview() {
        // ...
        undo = new Memento();
        // ...
    }

    public void undoChanges() {
        description = undo.getDescr();
        category = undo.getCateg();
        // ...
    }
}

class Application {
    ...
    public void method() {
        ...
        siteDescrBean.preview();
        siteDescrBean.setDescription(someDescription);
        siteDescrBean.setCategory(someCategoryId);
        ...
        siteDescrBean.undoChanges();
    }
}

Перед тем как войти в режим предпросмотра вызывается метод preview(). Метод preview, помимо создания экземпляра класса Memento, может содержать код для считывания данных из веб-интерфейса (или GUI) и отображения измененных данных.

В любом случае, после вызова preview можно делать любые изменения для значений полей description и category. Ну а с помощью метода undoChanges можно вернуть предыдущее состояние объекта SiteDescrBean.

Нюансы реализации паттерна

Обратите внимание, что класс Memento мы объявили как inner класс. Это сделано для того, чтобы иметь доступ к нестатическим полям обрамляющего класса. Что наглядно продемонстрировано в конструкторе класса Memento.

Для обеспечения инкапсуляции класс Memento объявлен как private.

Схожую с приведенным выше примером технику можно использовать при написании текстового редактора. Только поле undo (или как там вы его назовете) лучше описать как массив или список, если вы конечно не хотите создать аналог notepad.

После какждого изменения объекта можно делать вызов storeState или что-то в этом роде, который будет добавлять сущность "изменения" в некоторый список. Для отката изменений надо читать список в обратном порядке, отменять последнее изменение и удалять соответствующий элемент списка.

Немного теории и варианты использования

Сущность объекта для хранения состояния объекта называется Memento, это понятно. Но есть еще два объекта, которые учавствуют при реализации - это originator и caretaker.

Originator - это и есть объект, имеющий внутреннее состояние, которое мы хотим сохранять/возобновлять.

Caretaker - это объект, который работает с originator'ом и ожидает, что state последнего будет сохранен и при необходиомсти возвращен в предыдущее состояние.

В приведенном выше примере originator - это SiteDescrBean, а caretaker - это экземпляр класса Application.

Паттерн Memento полезен в таких случаях:

  • необходим функционал undo/redo;
  • при транзакциях (базы данных);
  • сохранение информации для последующего использования;
  • получение информации о состоянии объекта без нарушения инкапсуляции.

Кстати, любую имплементацию интерфейса java.io.Serializable тоже можно считать реализацией шаблона мементо.

Вызов метода writeObject у класса ObjectOutputStream запишет объект в некоторый поток байтов. С другой стороны, вызов ObjectInputStream.readObject() вернет объект, восстановленный из потока байтов. Вопросам сериализации и десериализации классов я обязательно посвящу отдельную статью.

На этом все. Жду ваших вопросов и замечаний.



Комментариев: 0

  Выйти

  * для публикации комментариев нужно