Wednesday, April 25, 2012

Шаблон проектирования Memento

При разработке приложений бывают ситуации, когда объекты должны уметь сохранять и восстанавливать свое состояние. Один из способов реализации этого - передача состояния одного объекта другому объекту. Но при этом возникают две проблемы:
  • раскрывается внутренняя структура объекта
  • другой объект может изменить полученное состояние
Намного лучше сохранять состояние объекта, используя специальный объект-контейнер, основанный на шаблоне Memento.

Memento


Тип: поведенческий шаблон

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

Альтернативные названия: Token, Snapshot

Реализация:
Originator - создает объект класса Memento и использует его впоследствии для восстановления своего состояния.

Memento - класс, внутренний по отношению к классу Originator, в котором сохраняется информация о состоянии последнего. Объем сохраняемой информации определяется классом Originator, который, кроме того, обладает эксклюзивными правами чтения содержимого Memento. Сохраненная информация о состоянии недоступна никакому другому объекту, кроме Originator.

StateHolder - объект, сохраняющий сведения о состоянии. Этому объекту ничего неизвестно об информации, сохраняемой внутри объекта Memento, — он лишь должен знать, что полученный им объект позволяет восстановить состояние объекта Originator.


Продолжая тему предыдущих статей про шаблоны проектирования, создадим класс, описывающий танк. Наш танк имеет два свойства - скорость (speed) и наносимое повреждение (power). Текущие значения этих свойств сохраняет в себе объект внутреннего класса TankMemento, который создается при вызове метода getMemento(). Метод setMemento() принимает объект типа Object и, если этот объект имеет тип TankMemento, меняет значения свойств объекта типа Tank:
public class Tank {
    private int speed;
    private int power;

    public Tank(int speed, int power) {
        this.speed = speed;
        this.power = power;
    }

    private class TankMemento {
        private int speedState;
        private int powerState;

        private TankMemento(int speedState, int powerState) {
            this.speedState = speedState;
            this.powerState = powerState;
        }
    }

    public Object getMemento() {
        return new TankMemento(speed, power);
    }

    public void setMemento(Object object) {
        if (object instanceof TankMemento) {
            TankMemento memento = (TankMemento) object;

            speed = memento.speedState;
            power = memento.powerState;
        }
    }

    public int getSpeed() {
        return speed;
    }

    public void setSpeed(int speed) {
        this.speed = speed;
    }

    public int getPower() {
        return power;
    }

    public void setPower(int power) {
        this.power = power;
    }

    public String getDescription() {
        return "Speed is " + speed + " km/h, power is " + power;
    }
}
Класс Tank - это наш Originator, TankMemento - соответственно Memento. Из-за простоты примера отдельного класса, соответствующего StateHolder, у нас вообще нет.

Для демонстрации работы шаблона создадим класс MementoDemo. Создадим экземпляр класса Tank с определенным состоянием - скоростью 50 км/ч и наносимым повреждением равным 50 единиц. Затем сохраним состояние созданного объекта tank, создав объект memento с помощью вызова метода getMemento(). Объект memento имеет тип Object, и его внутренняя реализация неизвестна. Он не имеет методов для изменения своих свойств. Известно лишь то, что он хранит состояние объекта tank. Когда мы сохранили состояние объекта tank, изменим его - допустим, что наш танк был поражен вражеским снарядом, вследствие чего его скорость и ударная мощь упали вдвое. Но через некоторое время мы смогли починить танк, и вернули его состояние к исходному с помощью вызова метода setMemento() и нашего объекта memento:
public class MementoDemo {

    public static void main(String[] atgs) {
        Tank tank = new Tank(50, 50);
        System.out.println("Initial tank description:");
        System.out.println(tank.getDescription() + "\n");

        Object memento = tank.getMemento();

        tank.setSpeed(25);
        tank.setPower(25);
        System.out.println("New tank description:");
        System.out.println(tank.getDescription() + "\n");

        tank.setMemento(memento);

        System.out.println("Restored tank description:");
        System.out.println(tank.getDescription());
    }
}

Запустим метод main():











Структура проекта:














Исходный код созданного приложения доступен по ссылке http://code.google.com/p/memento-demo/.

Рекомендуемые посты:

2 comments:

  1. Прекрасное объяснение. Спасибо большое
    Распишите и остальные шаблоны, пожалуйста

    ReplyDelete
    Replies
    1. Спасибо за отзыв! Возможно, в будущем!

      Delete