Знімок (шаблон проєктування)
Зні́мок (англ. Memento) — це шаблон проєктування, що належить до класу шаблонів поведінки і забезпечує можливість відновлення об'єкта до збереженого (попереднього) стану.
Призначення[ред. | ред. код]
Не порушуючи інкапсуляції, фіксує та виносить за межі об'єкта його внутрішній стан так, щоб пізніше можна було відновити з нього об'єкт.
Застосування[ред. | ред. код]
Слід використовувати шаблон Знімок у випадках, коли:
- необхідно зберегти миттєвий знімок стану об'єкта (або його частини), щоб згодом об'єкт можна було відтворити у тому ж самому стані;
- безпосереднє вилучення цього стану розкриває деталі реалізації та порушує інкапсуляцію об'єкта.
Структура[ред. | ред. код]
- Memento — контекст:
- зберігає внутрішній стан об'єкта Originator. Обсяг інформації, що зберігається, може бути різним та визначається потребами хазяїна;
- забороняє доступ усім іншим об'єктам окрім хазяїна. По суті знімок має два інтерфейси. Опікун Caretaker користується лише вузьким інтерфейсом знімку — він може лише передавати знімок іншим об'єктам. Навпаки, хазяїн користується широким інтерфейсом, котрий забезпечує доступ до всіх даних, необхідних для відтворення об'єкта (чи його частини) у попередньому стані. Ідеальний варіант — коли тільки хазяїну, що створив знімок, відкритий доступ до внутрішнього стану знімку;
- Originator — хазяїн:
- створює знімок, що утримує поточний внутрішній стан;
- використовує знімок для відтворення внутрішнього стану;
- CareTaker — опікун:
- відповідає за зберігання знімка;
- не проводить жодних операцій над знімком та не має уявлення про його внутрішній зміст.
Відносини[ред. | ред. код]
- опікун запитує знімок у хазяїна, деякий час тримає його у себе, опісля повертає хазяїну. Іноді цього не відбувається, бо хазяїн не має необхідності відтворювати свій попередній стан;
- знімки пасивні. Тільки хазяїн, що створив знімок, має доступ до інформації про стан.
Переваги[ред. | ред. код]
- Забезпечує спосіб запису внутрішнього стану об'єкта в окремому об'єкті, не порушуючи закону дизайну.
- Усуває потребу в багаторазовому створенні того ж об'єкта з єдиною метою збереження його стану.
- Спрощує Originator, даючи відповідальність за зберігання Memento серед Caretaker.
Недоліки[ред. | ред. код]
- Збереження та відновлення стану може зайняти багато часу.
- Об'єкт Memento повинен забезпечувати два типи інтерфейсів: вузький інтерфейс для Caretaker і широкий інтерфейс для Originator.
- Дозволяє іншому об'єкту довільно змінити стан об'єкта
Реалізація[ред. | ред. код]
C++[ред. | ред. код]
#include <iostream> #include <array> #include <Windows.h> using namespace std; // Стан містить здоров’я та кількість убитих монстрів struct State { float health; int amount_of_killed; State() :health(100), amount_of_killed(0) {}; friend ostream& operator<<(ostream & os, const State & s) { return os << "Health: " << s.health << " , killed " << s.amount_of_killed << '\n'; } }; // Memento — контекст // зберігає внутрішній стан об'єкта Originator class GameMemento { private: State state; public: GameMemento() {} GameMemento(State s) :state(s) {} State get_state() { return state; } }; // Originator — хазяїн // створює знімок, що утримує поточний внутрішній стан; // використовує знімок для відтворення внутрішнього стану; class GameOriginator { private: // Стан містить здоров’я та кількість убитих монстрів State state; public: void play() { // Імітуємо процес гри — // здоров’я повільно погіршується, а монстрів стає все менше state.health *= 0.9; state.amount_of_killed += 2; cout << state; } GameMemento game_save() { return GameMemento(state); } void load_game(GameMemento memento) { state = memento.get_state(); } }; //CareTaker — опікун //відповідає за зберігання знімка; //не проводить жодних операцій над знімком та не має уявлення про його внутрішній зміст. class Caretaker { private: GameOriginator game; array< GameMemento, 5 > quick_saves; int k; bool save_load; void check_save() { k < 5 ? k++ : k = 0; } // метод аби побачити коли була здійснена загрузка void check_load() { if (save_load == true) { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN); save_load = false; } else { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); } } public: Caretaker() : k(-1) {} void shoot() { check_load(); game.play(); } void F5() { check_save(); quick_saves[k] = game.game_save(); } void F9(int i = -1) { if (i < 0 || i > 5) i = k; game.load_game(quick_saves.at(i)); save_load = true; } }; void main() { Caretaker player; player.F5(); player.shoot(); player.F5(); player.shoot(); player.shoot(); player.shoot(); player.shoot(); player.F9(); player.shoot(); player.shoot(); player.F5(); player.F9(2); player.shoot(); player.shoot(); }
Джерела[ред. | ред. код]
- Design Patterns: Elements of Reusable Object-Oriented Software [Архівовано 9 листопада 2012 у Wayback Machine.]
- Андрій Будай Дизайн-патерни — просто, як двері [Архівовано 10 жовтня 2020 у Wayback Machine.]. — 2012. — 90 с.
Література[ред. | ред. код]
Алан Шаллоуей, Джеймс Р. Тротт. Шаблоны проектирования. Новый подход к объектно-ориентированному анализу и проектированию = Design Patterns Explained: A New Perspective on Object-Oriented Design. — М. : «Вильямс», 2002. — 288 с. — ISBN 0-201-71594-5.