Статьи

Шаблон дизайна Memento в Java — пример учебника

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

Шаблон Memento реализован с двумя объектами — Originator и Caretaker . Originator — это объект, состояние которого необходимо сохранить и восстановить, и он использует внутренний класс для сохранения состояния Object. Внутренний класс называется Memento и его private, поэтому к нему нельзя получить доступ из других объектов.

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

Один из лучших примеров из реальной жизни — это текстовые редакторы, где мы можем сохранять их данные в любое время и использовать отмену для восстановления их предыдущего сохраненного состояния. Мы реализуем ту же функцию и предоставим утилиту, в которой мы можем в любое время записать и сохранить содержимое в файл, а также восстановить его до последнего сохраненного состояния. Для простоты я не буду использовать операции ввода-вывода для записи данных в файл.

Класс оригинатора

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.journaldev.design.memento;
 
public class FileWriterUtil {
 
    private String fileName;
    private StringBuilder content;
 
    public FileWriterUtil(String file){
        this.fileName=file;
        this.content=new StringBuilder();
    }
 
    @Override
    public String toString(){
        return this.content.toString();
    }
 
    public void write(String str){
        content.append(str);
    }
 
    public Memento save(){
        return new Memento(this.fileName,this.content);
    }
 
    public void undoToLastSave(Object obj){
        Memento memento = (Memento) obj;
        this.fileName= memento.fileName;
        this.content=memento.content;
    }
 
    private class Memento{
        private String fileName;
        private StringBuilder content;
 
        public Memento(String file, StringBuilder content){
            this.fileName=file;
            //notice the deep copy so that Memento and FileWriterUtil content variables don't refer to same object
            this.content=new StringBuilder(content);
        }
    }
}

Обратите внимание на внутренний класс Memento и реализацию методов сохранения и отмены. Теперь мы можем продолжить реализацию класса смотрителя.

Ответственный класс

01
02
03
04
05
06
07
08
09
10
11
12
13
14
package com.journaldev.design.memento;
 
public class FileWriterCaretaker {
 
    private Object obj;
 
    public void save(FileWriterUtil fileWriter){
        this.obj=fileWriter.save();
    }
 
    public void undo(FileWriterUtil fileWriter){
        fileWriter.undoToLastSave(obj);
    }
}

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

Memento Test Class

Давайте напишем простую тестовую программу, которая будет использовать нашу реализацию сувенира.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.journaldev.design.memento;
 
public class FileWriterClient {
 
    public static void main(String[] args) {
 
        FileWriterCaretaker caretaker = new FileWriterCaretaker();
 
        FileWriterUtil fileWriter = new FileWriterUtil("data.txt");
        fileWriter.write("First Set of Data\n");
        System.out.println(fileWriter+"\n\n");
 
        // lets save the file
        caretaker.save(fileWriter);
        //now write something else
        fileWriter.write("Second Set of Data\n");
 
        //checking file contents
        System.out.println(fileWriter+"\n\n");
 
        //lets undo to last save
        caretaker.undo(fileWriter);
 
        //checking file content again
        System.out.println(fileWriter+"\n\n");
 
    }
 
}

Вывод вышеуказанной программы:

1
2
3
4
5
6
First Set of Data
 
First Set of Data
Second Set of Data
 
First Set of Data

Шаблон прост и легок в реализации, нужно позаботиться о том, чтобы класс Memento был доступен только для объекта Originator. Также в клиентском приложении мы должны использовать объект смотрителя для сохранения и восстановления состояния отправителя.

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

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

Ссылка: Шаблон дизайна Memento на Java — пример учебного курса от нашего партнера по JCG Панкаджа Кумара в блоге Developer Recipes .