Memento Design Pattern в Java

1. Общ преглед

В този урок ще научим какво представлява моделът за дизайн на Memento и как да го използваме.

Първо, ще преминем през малко теория. След това ще създадем пример, където ще илюстрираме използването на шаблона.

2. Какво представлява моделът за дизайн на Memento?

Моделът за дизайн на Memento, описан от Бандата на четирима в тяхната книга, е модел на поведенчески дизайн. Шаблонът за дизайн на Memento предлага решение за изпълнение на невъзможни действия. Можем да направим това, като запазим състоянието на обект в даден момент и го възстановим, ако извършените действия оттогава трябва да бъдат отменени.

На практика обектът, чието състояние трябва да бъде запазено, се нарича Оригинатор. Пазителят е обектът, задействащ запазването и възстановяването на състоянието, който се нарича Memento.

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

Нека видим бърза диаграма на класа, илюстрираща как различните обекти взаимодействат помежду си:

Както виждаме, Оригинаторът може да произвежда и консумира Memento. Междувременно Пазителят само задържа държавата, преди да я възстанови. Вътрешното представяне на Оригинатора се пази скрито от външния свят.

Тук използвахме едно поле, за да представим състоянието на оригинатора, въпреки че не сме ограничени до едно поле и бихме могли да използваме толкова полета, колкото е необходимо . Освен това състоянието, което се съхранява в обекта Memento, не трябва да съвпада с пълното състояние на оригинатора. Докато съхраняваната информация е достатъчна за възстановяване на състоянието на оригинатора, сме готови.

3. Кога да използвам Memento Design Pattern?

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

4. Пример за модел за спомен

4.1. Първоначална проба

Нека сега видим пример за модела за дизайн на Memento. Нека си представим, че имаме текстов редактор:

public class TextEditor { private TextWindow textWindow; public TextEditor(TextWindow textWindow) { this.textWindow = textWindow; } }

Той има текстов прозорец, който съдържа текущо въведения текст и осигурява начин за добавяне на още текст:

public class TextWindow { private StringBuilder currentText; public TextWindow() { this.currentText = new StringBuilder(); } public void addText(String text) { currentText.append(text); } }

4.2. Спомен

Сега, нека си представим, че искаме нашият текстов редактор да внедри някои функции за запазване и отмяна. Когато запазваме, искаме да запазим текущия си текст. По този начин, при отмяна на последващи промени, ще запазим запазения си текст.

За да направим това, ще използваме шаблона за дизайн на Memento. Първо ще създадем обект, съдържащ текущия текст на прозореца:

public class TextWindowState { private String text; public TextWindowState(String text) { this.text = text; } public String getText() { return text; } }

Този обект е нашият спомен. Както виждаме, ние избираме да използваме String вместо StringBuilder, за да предотвратим всякаква актуализация на текущия текст от външни лица.

4.3. Оригинатор

След това ще трябва да предоставим на TextWindow клас с методи за създаване и консумиране на обекта Memento, превръщайки TextWindow в наш оригинатор :

public TextWindowState save() { return new TextWindowState(wholeText.toString()); } public void restore(TextWindowState save) { currentText = new StringBuilder(save.getText()); }

Методът save () ни позволява да създадем обекта, докато методът restore () го консумира за възстановяване на предишното състояние.

4.4. Служител

И накрая, трябва да актуализираме нашия клас TextEditor . Като Служител, той ще държи състоянието на Оригинатора и ще поиска да го възстанови, когато е необходимо:

private TextWindowState savedTextWindow; public void hitSave() { savedTextWindow = textWindow.save(); } public void hitUndo() { textWindow.restore(savedTextWindow); }

4.5. Тестване на разтвора

Нека да видим дали работи чрез пробно изпълнение. Представете си, че добавяме малко текст към редактора си, запазваме го, след това добавяме още и накрая отменяме. За да постигнем това, ще добавим метод print () в нашия TextEditor, който връща низ от текущия текст:

TextEditor textEditor = new TextEditor(new TextWindow()); textEditor.write("The Memento Design Pattern\n"); textEditor.write("How to implement it in Java?\n"); textEditor.hitSave(); textEditor.write("Buy milk and eggs before coming home\n"); textEditor.hitUndo(); assertThat(textEditor.print()).isEqualTo("The Memento Design Pattern\nHow to implement it in Java?\n");

Както виждаме, последното изречение не е част от текущия текст, тъй като Memento беше запазен преди да го добавите.

5. Заключение

В тази кратка статия обяснихме Memento Design Pattern и за какво може да се използва. Разгледахме и пример, илюстриращ използването му в прост текстов редактор.

Пълният код, използван в тази статия, може да бъде намерен в GitHub.