Wzór konstrukcyjny silnika cofania

Piszę narzędzie do modelowania strukturalnego dla aplikacji do inżynierii lądowej. Mam jedną ogromną klasę modelu reprezentującą cały budynek, która zawiera zbiory węzłów, elementów linii, obciążeń itp. które są również klasami niestandardowymi.

Zakodowałem już silnik cofania, który zapisuje głęboką kopię po każdej modyfikacji modelu. Teraz zacząłem myśleć, czy mógłbym kodować inaczej. Zamiast zapisywać Głębokie kopie, mógłbym zapisać listę każdej akcji modyfikującej z odpowiednim modyfikatorem wstecznym. Żebym mógł zastosować modyfikatory odwrotne do bieżącego modelu do cofnięcia lub modyfikatory do ponownego wykonania.

Wyobrażam sobie, jak można wykonywać proste polecenia, które zmieniają właściwości obiektu itp. Ale co ze złożonymi poleceniami? Jak wstawianie nowych obiektów węzła do modelu i dodawanie niektórych obiektów liniowych, które zachowują odniesienia do nowych węzłów.

Jak można to wdrożyć?

Author: Ozgur Ozcitak, 2008-09-08

22 answers

Większość przykładów, które widziałem, używa do tego wariantu wzorca poleceń. Każda akcja użytkownika, która jest niemożliwa do wykonania, otrzymuje własną instancję polecenia ze wszystkimi informacjami, aby wykonać akcję i cofnąć ją. Następnie można zachować listę wszystkich komend, które zostały wykonane i można je cofnąć jeden po drugim.

 84
Author: Mendelt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-29 00:07:52

Myślę, że zarówno memento, jak i command nie są praktyczne, gdy mamy do czynienia z modelem o rozmiarze i zakresie, który implikuje OP. Będą działać, ale to będzie dużo pracy, aby utrzymać i rozszerzyć.

Dla tego typu problemów, myślę, że należy zbudować wsparcie dla modelu danych, aby obsługiwać punkty kontrolne różnicowe dla każdego obiektu zaangażowanego w model. Zrobiłem to raz i działało bardzo sprawnie. Najważniejsze, co musisz zrobić, to unikać bezpośredniego korzystania z wskaźniki lub odniesienia w modelu.

Każde odniesienie do innego obiektu używa jakiegoś identyfikatora (np. liczby całkowitej). Ilekroć obiekt jest potrzebny, wyszukuje się bieżącą definicję obiektu z tabeli. Tabela zawiera powiązaną listę dla każdego obiektu, która zawiera wszystkie poprzednie wersje, wraz z informacjami o tym, dla którego punktu kontrolnego były one aktywne.

Implementacja Cofnij/Ponów jest prosta: wykonaj swoją akcję i utwórz nowy punkt kontrolny; wycofaj wszystkie wersje obiektów do poprzedniego punktu kontrolnego.

To wymaga pewnej dyscypliny w kodzie, ale ma wiele zalet: nie potrzebujesz głębokich kopii, ponieważ robisz różnicowe przechowywanie stanu modelu; możesz określić ilość pamięci, której chcesz użyć ( Bardzo ważne dla rzeczy takich jak modele CAD) przez liczbę redo lub używaną pamięć; bardzo skalowalny i łatwy w utrzymaniu dla funkcji, które działają na modelu, ponieważ nie muszą nic robić, aby zaimplementować undo/redo.

 29
Author: Jeff Kotula,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-17 21:09:48

Jeśli mówisz o GoF, wzór Memento konkretnie odnosi się do undo.

 17
Author: Andy Whitfield,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-08 15:00:38

Jak stwierdzili inni, wzorzec poleceń jest bardzo wydajną metodą implementacji Undo / Redo. Ale jest ważna zaleta, którą chciałbym wspomnieć o wzorze dowodzenia.

Podczas implementacji undo / redo za pomocą wzorca poleceń, można uniknąć dużych ilości zduplikowanego kodu przez abstrakcję (do pewnego stopnia) operacji wykonywanych na danych i wykorzystać te operacje w systemie undo/redo. Np. w edytorze tekstu cut i paste są komplementarnymi poleceniami (oprócz z zarządzania schowkiem). Innymi słowy, operacja cofania dla cięcia jest wklejana, a operacja cofania dla wklejania jest cięta. Dotyczy to znacznie prostszych operacji, takich jak pisanie i usuwanie tekstu.

Kluczem jest to, że możesz użyć systemu Cofnij/Ponów jako głównego systemu poleceń dla Twojego edytora. Zamiast pisać system, taki jak "Utwórz Cofnij obiekt, zmodyfikuj dokument", możesz "utworzyć Cofnij obiekt, wykonać operację ponów na Cofnij obiekt, aby zmodyfikować dokument".

Teraz, co prawda, wiele osób myśli sobie: "Cóż, czy nie jest to część wzorca dowodzenia?"Tak, ale widziałem zbyt wiele systemów poleceń, które mają dwa zestawy poleceń, jeden do natychmiastowych operacji, a drugi do cofania/ponawiania. Nie mówię, że nie będzie poleceń, które są specyficzne dla natychmiastowych operacji i Cofnij/Ponów, ale zmniejszenie duplikacji sprawi, że kod będzie łatwiejszy do utrzymania.

 15
Author: Torlack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-08 17:12:38

Możesz odwołać się do Paint.NET kod do ich cofania - mają naprawdę ładny system cofania. Jest to prawdopodobnie nieco prostsze niż to, czego potrzebujesz, ale może dać ci kilka pomysłów i wskazówek.

- Adam

 8
Author: Adam Davis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-08 14:05:50

Może to być przypadek, w którym CSLA ma zastosowanie. Został zaprojektowany w celu zapewnienia kompleksowej obsługi cofania obiektów w aplikacjach Windows Forms.

 7
Author: Eric Z Beard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-08 14:10:15

Zaimplementowałem złożone systemy cofania z powodzeniem, używając wzorca Memento - bardzo łatwo i ma tę zaletę, że naturalnie zapewnia również Framework ponownego wykonywania. Bardziej subtelną korzyścią jest to, że zagregowane akcje mogą być zawarte również w jednym cofnięciu.

W skrócie, masz dwa stosy przedmiotów memento. Jeden do cofnięcia, drugi do ponownego wykonania. Każda operacja tworzy nowe memento, które idealnie będzie niektóre wywołania, aby zmienić stan modelu, dokumentu (lub cokolwiek innego). To zostaje dodane do stosu undo. Gdy wykonujesz operację cofania, oprócz wykonania operacji Cofania na obiekcie Memento w celu ponownej zmiany modelu, usuwasz obiekt ze stosu cofania i wepchasz go bezpośrednio na stosie powtarzania.

Sposób implementacji metody zmiany stanu dokumentu zależy całkowicie od twojej implementacji. ChangeColour(r,g,b)), poprzedza je zapytaniem, aby uzyskać i zapisać odpowiedni stan. Ale wzór będzie również wspierać wykonywanie głębokich kopii, migawek pamięci, tworzenie plików tymczasowych itp-wszystko zależy od Ciebie, jak to jest po prostu implementacja metody wirtualnej.

Aby wykonać zagregowane akcje (np. user Shift-wybiera obciążenie obiektów do wykonania operacji, np. delete, rename, change attribute), kod tworzy nowy stos cofania jako pojedynczą memento i przekazuje go do rzeczywistej operacji, aby dodać poszczególne operacje. Więc twoje metody działania nie muszą (A) mieć globalnego stosu, aby się martwić about I (b) mogą być kodowane tak samo, niezależnie od tego, czy są wykonywane osobno, czy jako część jednej operacji zbiorczej.

Wiele systemów cofania jest tylko w pamięci, ale jeśli chcesz, możesz usunąć stos cofania.

 6
Author: Greg Whitfield,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-08 17:03:29

Właśnie czytałem o wzorze poleceń w mojej książce programistycznej agile-może to ma potencjał?

Każde polecenie może zaimplementować interfejs polecenia (który ma metodę Execute ()). Jeśli chcesz cofnąć, możesz dodać metodę cofania.

Więcej informacji tutaj

 5
Author: Dave Arkell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-08 14:02:08

Jestem z Mendelt Siebenga na fakt, że należy użyć wzorca poleceń. Wzór, którego użyłeś, był wzorem Memento, który z czasem może i stanie się bardzo marnotrawny.

Ponieważ pracujesz nad aplikacją wymagającą dużej ilości pamięci, powinieneś być w stanie określić, ile pamięci może zająć silnik cofania, ile poziomów cofania zostanie zapisanych lub w jakiej pamięci będą przechowywane. Jeśli tego nie zrobisz, wkrótce napotkasz błędy wynikające z tego, że maszyna nie ma pamięci.

Radzę sprawdzić, czy istnieje framework, który stworzył już model undo w wybranym przez Ciebie języku / frameworku programowania. Miło jest wymyślać nowe rzeczy, ale lepiej wziąć coś już napisanego, debugowanego i przetestowanego w rzeczywistych scenariuszach. Pomogłoby, gdybyś dodał to, co piszesz, aby ludzie mogli polecić frameworki, które znają.

 4
Author: Omer van Kloeten,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:11

Projekt Codeplex :

Jest to prosty framework do dodawania funkcji cofania/ponawiania do aplikacji, oparty na klasycznym wzorcu projektowania poleceń. Obsługuje akcje scalania, zagnieżdżone transakcje, opóźnione wykonanie (wykonanie na najwyższym poziomie zatwierdzania transakcji) i możliwą nieliniową historię cofania (gdzie możesz mieć do wyboru wiele akcji do ponownego wykonania).

 3
Author: sg7,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-13 00:11:01

Większość przykładów, które czytałem, robi to za pomocą polecenia lub wzorca memento. Ale można to zrobić również bez wzorców projektowych za pomocą prostej deque-structure.

 2
Author: Patrik Svensson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-08 16:08:18

Sprytnym sposobem obsługi funkcji undo, który sprawi, że Twoje oprogramowanie będzie również odpowiednie do współpracy z wieloma użytkownikami, jest wdrożenie transformacji operacyjnej struktury danych.

Pojęcie to nie jest zbyt popularne, ale dobrze zdefiniowane i użyteczne. Jeśli definicja wygląda dla Ciebie zbyt abstrakcyjnie, Ten projekt jest udanym przykładem tego, jak transformacja operacyjna dla obiektów JSON jest zdefiniowana i zaimplementowana w Javascript

 2
Author: danza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-05 12:09:40

Dla odniesienia, oto prosta implementacja wzorca poleceń dla Undo / Redo w C#: prosty System undo / redo dla C#.

 2
Author: Tomas Andrle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-13 00:05:52

Użyliśmy ponownie wczytania pliku i zapisania kodu serializacji dla "obiektów" w wygodnej formie do zapisania i przywrócenia całego stanu obiektu. Wypychamy te serializowane obiekty na stosie undo – wraz z informacjami o wykonanej operacji i podpowiedziami na temat cofania tej operacji, jeśli nie ma wystarczającej ilości informacji zebranych z serializowanych danych. Cofanie i ponowne robienie często polega na zastąpieniu jednego obiektu innym (w teorii).

Było wiele błędów z powodu wskaźników (C++) do obiektów, które nigdy nie zostały naprawione podczas wykonywania dziwnych sekwencji undo redo (te miejsca nie zostały zaktualizowane do bezpieczniejszych undo świadomych "identyfikatorów"). Pluskwy w tym obszarze często ...hmmm... interesujące.

Niektóre operacje mogą być szczególnymi przypadkami dla szybkości / wykorzystania zasobów - jak wymiarowanie rzeczy, przesuwanie rzeczy wokół.

Multi-selekcja zapewnia również kilka ciekawych komplikacji. Na szczęście mieliśmy już koncepcję grupowania w kodzie. Kristopher Johnson komentarz o podkategoriach jest ładny blisko tego, co robimy.

 1
Author: Aardvark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-08 16:43:12

Musiałem to zrobić pisząc solver do gry logicznej peg-jump. Każdy ruch był obiektem polecenia, który zawierał wystarczająco dużo informacji, aby można było go wykonać lub cofnąć. W moim przypadku było to tak proste, jak zapisanie pozycji wyjściowej i kierunku każdego ruchu. Następnie zapisałem wszystkie te obiekty w stosie, aby program mógł łatwo cofnąć tyle ruchów, ile potrzeba podczas cofania.

 1
Author: Bill the Lizard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-11 15:16:34

Możesz wypróbować gotową implementację Undo / Redo pattern w PostSharp. https://www.postsharp.net/model/undo-redo

Umożliwia dodanie funkcji cofania/ponawiania do aplikacji bez samodzielnego implementowania wzorca. Wykorzystuje Zapisywalny wzorzec do śledzenia zmian w modelu i współpracuje z wzorzec INotifyPropertyChanged, który jest również realizowany w PostSharp.

Jesteś wyposażony w kontrolki interfejsu użytkownika i możesz zdecydować, jaka nazwa i szczegółowość każdego operacja będzie.

 1
Author: Antonín Procházka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-04 10:55:55

Pracowałem kiedyś nad aplikacją, w której wszystkie zmiany wprowadzone przez polecenie do modelu aplikacji (tj. CDocument... używaliśmy MFC) zostały utrzymane na końcu polecenia poprzez aktualizację pól w wewnętrznej bazie danych utrzymywanej w modelu. Nie musieliśmy więc pisać oddzielnego kodu cofania/ponawiania dla każdej akcji. Stos undo po prostu zapamiętał podstawowe klucze, nazwy pól i stare wartości za każdym razem, gdy rekord został zmieniony (na końcu każdego polecenia).

 0
Author: Agnel Kurian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-08 15:17:19

Pierwsza część wzorców projektowych (GoF, 1994) ma przypadek użycia do implementacji undo / redo jako wzorca projektowego.

 0
Author: Peter Turner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-17 20:46:05

Możesz zrobić swój początkowy pomysł performance.

Użyj trwałych struktur danych i trzymaj się listy odniesień do starego stanu wokół . (Ale to naprawdę działa, jeśli operacje wszystkie dane w klasie stanu są niezmienne, a wszystkie operacje na niej zwracają nową wersję - - - ale nowa wersja nie musi być głęboką kopią, wystarczy zastąpić zmienione części 'copy-on-write'.)

 0
Author: Matthias,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-26 06:36:06

Znalazłem wzór polecenia bardzo przydatny tutaj. Zamiast implementować kilka odwrotnych poleceń, używam rollback z opóźnionym wykonaniem na drugiej instancji mojego API.

Takie podejście wydaje się rozsądne, jeśli zależy ci na niskim wysiłku w implementacji i łatwej konserwacji (i możesz sobie pozwolić na dodatkową pamięć dla drugiej instancji).

Zobacz tutaj przykład: https://github.com/thilo20/Undo/

 0
Author: Thilo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-12 23:24:06

Nie wiem, czy to ci się przyda, ale kiedy musiałem zrobić coś podobnego na jednym z moich projektów, skończyło się na pobraniu UndoEngine z http://www.undomadeeasy.com [2]} - Wspaniały silnik i naprawdę nie dbałem zbytnio o to, co było pod maską - po prostu działało.

 -1
Author: NativeBreed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-28 15:56:31

Moim zdaniem UNDO/REDO można by zaimplementować na 2 sposoby. 1. Poziom komend (zwany poziomem komend Cofnij/Ponów) 2. Poziom dokumentu (zwany globalnym Undo/Redo)

Poziom komend: jak wskazuje wiele odpowiedzi, można to skutecznie osiągnąć za pomocą wzorca Memento. Jeśli polecenie obsługuje również dziennikowanie akcji, ponowne wykonanie jest łatwe.

Ograniczenie: gdy zakres polecenia jest wyłączony, undo / redo jest niemożliwe, co prowadzi do poziomu dokumentu(globalnego) undo / redo

Myślę, że Twoja sprawa zmieściłaby się w globalnym undo / redo, ponieważ jest odpowiednia dla modelu, który wymaga dużo miejsca w pamięci. Jest to również odpowiednie do selektywnego cofania/ponawiania. Istnieją dwa prymitywne typy

  1. cała pamięć Cofnij/Ponów
  2. poziom obiektu Undo Redo

W "All memory Undo / Redo" cała pamięć jest traktowana jako połączone dane (takie jak drzewo, lista lub wykres), a pamięcią zarządza aplikacja, a nie OS. Tak więc nowe i usuwane operatory, jeśli w C++ są przeciążone, aby zawierały bardziej konkretne struktury, aby skutecznie implementować operacje, takie jak a. jeśli dowolny węzeł jest modyfikowany, b.przechowywanie i czyszczenie danych itp., Sposób jej działania polega zasadniczo na skopiowaniu całej pamięci (przy założeniu, że alokacja pamięci jest już zoptymalizowana i zarządzana przez aplikację przy użyciu zaawansowanych algorytmów) i zapisaniu jej w stosie. Jeśli wymagana jest kopia pamięci, struktura drzewa jest kopiowana w oparciu o potrzebę posiadania płytka lub głęboka Kopia. Głęboka kopia jest wykonywana tylko dla tej zmiennej, która jest modyfikowana. Ponieważ każda zmienna jest przydzielana za pomocą alokacji niestandardowej, aplikacja ma ostatnie słowo, kiedy ją usunąć, jeśli zajdzie taka potrzeba. Rzeczy stają się bardzo interesujące, jeśli mamy do podziału Undo/Redo, gdy tak się składa, że musimy programowo-selektywnie cofnąć/ponowić zestaw operacji. W tym przypadku tylko te nowe zmienne lub usunięte lub zmodyfikowane zmienne otrzymują flagę, dzięki czemu tylko Cofnij/Ponów cofa / przywraca pamięć Rzeczy stają się jeszcze bardziej interesujące, jeśli musimy wykonać częściowe cofanie/ponawianie wewnątrz obiektu. W takim przypadku używa się nowszej koncepcji "wzorca odwiedzających". Nazywa się"Undo/ponów poziom obiektu"

  1. poziom obiektu Undo / Redo: gdy wywoływane jest powiadomienie o cofnięciu / przywróceniu, każdy obiekt implementuje operację strumieniową, w której streamer pobiera z obiektu stare dane/nowe dane, które są zaprogramowane. Dane, które nie zostaną naruszone, pozostają niezakłócone. Każdy obiekt otrzymuje jako argument streamer i wewnątrz wywołania UNDo / Redo, przesyła / usuwa strumieniowo dane obiektu.

Zarówno 1 jak i 2 mogą mieć takie metody jak 1. Przed wyjazdem() 2. AfterUndo() 3. BeforeRedo() 4. Po (). Metody te muszą być opublikowane w podstawowym poleceniu Undo / redo ( nie poleceniu kontekstowym), tak aby wszystkie obiekty implementowały te metody, aby uzyskać określoną akcję.

Dobrą strategią jest stworzenie hybrydy 1 i 2. Piękno jest to, że te metody (1 i 2) same używają wzorców poleceń

 -1
Author: Parthasarathy SRINIVASAN,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-06 13:29:54