Modèle de conception pour Annuler Moteur
Je suis en train d'écrire une modélisation structurelle de l'outil pour un civil enginering application. J'ai un énorme modèle de classe représentant l'ensemble du bâtiment, qui intègrent les collections de nœuds, éléments de ligne, les charges, etc. qui sont aussi des classes personnalisées.
J'ai déjà codé un annuler moteur qui enregistre une copie en profondeur après chaque modification du modèle. Maintenant, j'ai commencé à penser si je pouvais avoir codé différemment. Au lieu de sauver la profondeur de copies, je pourrais peut-être enregistrer une liste de chaque modificateur d'action avec un correspondant inverse modificateur. Pour que je puisse appliquer l'inverse des modificateurs pour le modèle actuel pour annuler, ou les modificateurs de refaire.
Je peux imaginer comment vous réaliser simplement des commandes qui modifient les propriétés de l'objet, etc. Mais comment, sur la complexité des commandes? Comme l'insertion de nouveaux objets de nœud pour le modèle et l'ajout de quelques objets qui conservent des références à la nouvelle nœuds.
Comment peut-on aller sur la mise en œuvre de qui?
- Si j'ajoute le commentaire "Annuler Algorthim" qui va le faire donc je peux rechercher "Algorithme d'Annulation" et la trouver? C'est ce que j'ai cherché et j'ai trouvé quelque chose de fermé comme un doublon.
- le foin,j'ai aussi envie de développer undo/redo dans l'application que nous développons.Nous utilisons QT4 cadre et le besoin d'avoir beaucoup de complexes undo/redo actions..je me demandais , avez-vous réussi à l'aide de la Commande-Modèle ?
- Il a travaillé, mais il n'était pas facile. La partie la plus difficile a été de garder une trace de références. Par exemple, lorsqu'une Image de l'objet est supprimé, ses objets enfants: les Nœuds, les Charges agissant sur celle-ci et beaucoup d'autres attributions d'utilisateur nécessaires pour être gardé pour être réinséré quand annulée. Mais certains de ces objets enfants ont été partagés avec d'autres objets, et annuler/rétablir la logique est devenue très complexe. Si le modèle n'était pas très grande, je voudrais garder le souvenir de l'approche; il est beaucoup plus facile à mettre en œuvre.
- c'est un plaisir de problème, pensez à la façon dont le code source de repos le faire, comme svn (ils gardent les différences entre les commits).
Vous devez vous connecter pour publier un commentaire.
La plupart des exemples que j'ai vu utiliser une variante de la Commande-Modèle pour cela. Chaque utilisateur-action, ce qui est infaisable obtient sa propre instance de commande avec toutes les informations pour exécuter l'action, et de rouler de nouveau. Vous pouvez ensuite conserver une liste de toutes les commandes qui ont été exécutés, et vous pouvez rouler une par une.
Je pense que les deux memento et de commande ne sont pas pratique quand vous avez affaire à un modèle de la taille et de la portée que l'OP implique. Ils allaient travailler, mais il serait beaucoup de travail pour maintenir et à étendre.
Pour ce type de problème, je pense que vous avez besoin pour construire à l'appui de votre modèle de données à l'appui de différentiel de points de contrôle pour chaque objet impliqués dans le modèle. Je l'ai fait une fois et cela a fonctionné très lisse. La plus grande chose que vous avez à faire est d'éviter l'utilisation directe des pointeurs ou des références dans le modèle.
Chaque référence à un autre objet utilise un identificateur (comme un entier). Chaque fois que l'objet est nécessaire, vous la recherche de la définition actuelle de l'objet à partir d'une table. Le tableau contient une liste chaînée pour chaque objet qui contient toutes les versions précédentes, ainsi que les informations concernant le point de contrôle qui ils étaient actifs pour.
La mise en œuvre de undo/redo est simple: Faire de votre action et établir un nouveau point de contrôle; la restauration de toutes les versions d'objets au point de contrôle précédent.
Il faut une certaine discipline dans le code, mais il a de nombreux avantages: vous n'avez pas besoin profond d'exemplaires depuis que vous faites différentiel de stockage du modèle de l'état; vous pouvez le champ d'application de la quantité de mémoire que vous souhaitez utiliser (très important pour des choses comme des modèles CAO) soit du nombre de rétablissements ou de la mémoire utilisée; très évolutif et très peu d'entretien pour les fonctions qui opèrent sur le modèle, car ils n'ont pas besoin de faire quelque chose pour mettre en œuvre undo/redo.
Si vous parlez GoF, le Memento modèle traite spécifiquement de l'annuler.
Comme d'autres l'ont dit, le modèle de commande est une méthode très puissante de la mise en œuvre de Undo/Redo. Mais il est important avantage, je tiens à mentionner à la commande modèle.
Lors de la mise en œuvre de undo/redo en utilisant le modèle de commande, vous pouvez éviter de grandes quantités de code dupliqué à l'abstraction (à un degré) les opérations effectuées sur les données et d'utiliser ces opérations dans la fonction annuler/rétablir le système. Par exemple dans un éditeur de texte couper et coller sont complémentaires des commandes (à l'exception de la gestion du presse-papiers). En d'autres termes, l'opération d'annulation pour une coupe pâte et l'opération d'annulation pour une pâte est coupée. Cela s'applique à beaucoup plus simple les opérations de saisie et supprimer du texte.
La clé ici est que vous pouvez utiliser votre undo/redo système comme le principal système de commande de votre éditeur. Au lieu d'écrire le système comme "créer annuler un objet, de modifier le document" vous pouvez "créer annuler objet, exécuter refaire l'opération sur annuler objet de modifier le document".
Maintenant, il est vrai, beaucoup de gens pensent à eux-mêmes "eh Bien, duh, n'est-ce pas en partie du modèle de commande?" Oui, mais j'ai vu trop de nombreux systèmes de commandement qui ont deux ensembles de commandes, l'une pour les opérations immédiates et un autre ensemble pour annuler/refaire. Je ne dis pas qu'il n'y aura pas de commandes spécifiques aux opérations immédiates et undo/redo, mais en réduisant la duplication rendre le code plus maintenable.
paste
commecut
^-1.Vous pouvez vous reporter à la Paint.NET code pour leur undo - ils ont un très bon système d'annulation. C'est sans doute un peu plus simple que ce que vous aurez besoin, mais il peut vous donner quelques idées et des lignes directrices.
-Adam
Ce pourrait être un cas où L'AAPC est applicable. Il a été conçu pour fournir complexe annuler soutien aux objets dans les applications Windows Forms.
J'ai mis en place complexe annuler les systèmes avec succès en utilisant le Souvenir modèle très facile, et a l'avantage d'naturellement fournir un Redo cadre trop. Plus subtil avantage est que l'agrégation des actions peut être contenue dans un seul Annuler trop.
En un mot, vous avez deux piles de memento des objets. Un pour Annuler, l'autre pour les Refaire. Chaque opération crée un nouveau memento, qui, dans l'idéal sera de certains appels à modifier l'état de votre modèle de document (ou autre). Cela s'ajoute à la pile d'annulation. Lorsque vous effectuez une opération d'annulation, en plus de l'exécution de l'Annulation de la dernière action sur le Souvenir de l'objet de modifier le modèle de nouveau, vous avez également pop de l'objet sur la pile d'Annulation et de le pousser à droite sur la pile de rétablissement.
Comment la méthode pour changer l'état de votre document est mis en œuvre dépend complètement de votre mise en œuvre. Si vous pouvez tout simplement faire un appel d'API (par exemple ChangeColour(r,g,b)), puis faites-la précéder d'une requête pour obtenir et enregistrer l'état correspondant. Mais le modèle sera également en charge de décisions profonde des copies, des instantanés de la mémoire, temp de création de fichier, etc - c'est à vous qu'il s'en est tout simplement une méthode virtuelle de la mise en œuvre.
Faire agrégation d'actions (par exemple, l'utilisateur Maj-Sélectionne une charge d'objets pour faire une opération, par exemple, supprimer, renommer, modifier l'attribut), votre code crée une nouvelle pile d'Annulation comme un seul souvenir, et le passe à l'opération pour ajouter un individu opérations. Si vos méthodes d'action n'avez pas besoin de (a) à l'échelle mondiale pour pile à s'inquiéter et (b) peuvent être codés de la même s'ils sont exécutés dans l'isolement ou dans le cadre d'une opération agrégat.
Beaucoup d'annuler les systèmes sont en mémoire seulement, mais vous pourrait persister, la pile d'annulation si vous le souhaitez, je suppose.
Juste lu sur le modèle de commande dans mon développement agile livre - c'est peut-être eu potentiel?
Vous pouvez avoir toutes les commandes de mettre en œuvre l'interface de commande (qui a une méthode Execute ()). Si vous souhaitez annuler, vous pouvez ajouter une Annulation de la méthode.
plus d'infos ici
Je suis avec Mendelt Siebenga sur le fait que vous devez utiliser le Modèle de Commande. Le schéma que vous avez utilisé était le Souvenir du Motif, qui peut et va devenir très inutile au fil du temps.
Puisque vous travaillez sur un mémoire intensive des applications, vous devriez être en mesure de spécifier la quantité de mémoire de l'annulation du moteur est autorisé à prendre, combien de niveaux d'annulation sont enregistrées ou une partie de la mémoire à laquelle ils seront conservées. Il ne faut pas faire cela, vous allez bientôt faire face à des erreurs résultant de la machine en cours de la mémoire.
Je vous conseille de vérifier s'il existe un cadre déjà créé un modèle pour les annulations dans le langage de programmation /cadre de votre choix. C'est sympa d'inventer de nouvelles choses, mais il vaut mieux prendre quelque chose de déjà écrit, une mise au point et testé dans des scénarios réels. Il serait utile si vous avez ajouté ce que vous écrivez cela, afin que les gens peuvent recommander des cadres de ils savent.
Projet Codeplex:
C'est un cadre simple pour ajouter Annuler/Refaire des fonctionnalités à vos applications, basé sur le classique de Commande modèle de conception. Il prend en charge la fusion actions, les transactions imbriquées, retards dans l'exécution (exécution sur le dessus de niveau de la validation des transactions) et de la possible non-linéaire historique d'annulation (où vous pouvez avoir un choix de plusieurs actions à refaire).
La plupart des exemples que j'ai lu de le faire à l'aide de la commande ou un souvenir modèle. Mais vous pouvez le faire sans les modèles de conception de trop avec un simple deque-structure.
Un moyen astucieux pour gérer les annuler, ce qui rendrait votre logiciel est également adapté pour le multi-utilisateur la collaboration, la mise en oeuvre d'un la transformation opérationnelle de la structure de données.
Ce concept n'est pas très populaire, mais bien définis et utile. Si la définition est trop abstrait pour vous, ce projet est un bon exemple de comment une transformation opérationnelle pour les objets JSON est défini et mis en œuvre en Javascript
Pour référence, voici une simple mise en œuvre du modèle de Commande pour Annuler/Refaire en C#: Simple undo/redo système pour C#.
Nous avons réutilisé le chargement de fichier et enregistrer le code de sérialisation pour des “objets” pour une forme pratique pour enregistrer et restaurer l'ensemble de l'état d'un objet. Nous pousser ces objets sérialisés dans la pile d'annulation, avec quelques informations sur ce que l'opération a été effectuée et des conseils sur annuler-ing que l'opération s'il n'y a pas assez d'informations obtenues à partir des données sérialisées. Défaire et Refaire, il suffit souvent de remplacer un objet à un autre (en théorie).
Il y a eu beaucoup BEAUCOUP de bugs dus à des pointeurs C++ (C++) pour les objets qui n'ont jamais été fixe-up pendant que vous faites de drôles de undo redo séquences (ces endroits qui ne sont pas mis à jour plus sûr annuler conscient “identifiants”). Bugs dans cette zone souvent ...ummm... intéressant.
Certaines opérations peuvent être des cas particuliers pour la vitesse/l'utilisation des ressources - comme le dimensionnement des choses, des objets en mouvement autour de.
Multi-sélection de l'offre d'intéressants complications. Heureusement, nous avions déjà un groupement concept dans le code. Kristopher Johnson commentaire à propos de la sous-éléments est assez proche de ce que nous faisons.
J'ai dû le faire lors de la rédaction d'un solveur pour un peg-saut jeu de puzzle. J'ai fait chaque déplacement d'un objet de Commande de suffisamment d'informations qu'il pourrait être fait ou défait. Dans mon cas, c'était aussi simple que de stocker la position de départ et la direction de chaque mouvement. J'ai ensuite stockés tous ces objets dans une pile de sorte que le programme pourrait facilement annuler autant de coups qu'il faut tout retour en arrière.
Vous pouvez essayer de ready-made de mise en œuvre de Undo/Redo modèle de PostSharp. https://www.postsharp.net/model/undo-redo
Il vous permet d'ajouter annuler/rétablir la fonctionnalité de votre application sans la mise en œuvre de la patron de vous-même. Il utilise Enregistrable modèle à suivre les changements dans votre modèle, et il fonctionne avec INotifyPropertyChanged modèle qui est également mis en place de PostSharp.
Vous sont fournis avec des contrôles d'INTERFACE utilisateur et vous pouvez décider de ce que le nom et la granularité de chaque opération sera.
Une fois, j'ai travaillé sur une application dans laquelle toutes les modifications effectuées par une commande à l'application du modèle (c'est à dire CDocument... nous avons été à l'aide de MFC) ont persisté jusqu'à la fin de la commande en mettant à jour les champs dans une base de données maintenue à l'intérieur du modèle. Donc nous n'avons pas d'écrire une fonction undo/redo code pour chaque action. La pile d'annulation simplement rappelé les clés primaires, les noms de champ et les anciennes valeurs à chaque fois qu'un enregistrement a été modifié (à la fin de chaque commande).
La première section des Modèles de Conception (GoF, 1994) a un cas d'utilisation pour la mise en œuvre de la fonction annuler/rétablir comme un modèle de conception.
Vous pouvez faire de votre idée initiale performant.
Utilisation persistante des structures de données, et le bâton avec le maintien d'un liste de références à l'ancien état. (Mais qui ne fonctionne vraiment si les opérations de toutes les données de votre classe d'état sont immuables, et toutes les opérations sur le retour d'une nouvelle version---mais la nouvelle version n'a pas besoin d'être une copie en profondeur, il suffit de remplacer le changé des pièces de copie à l'écriture".)
J'ai trouvé le modèle de Commande très utile ici. Au lieu de la mise en œuvre de plusieurs inverser les commandes, je suis en utilisant la restauration avec des retards dans l'exécution sur une deuxième instance de mon API.
Cette approche semble raisonnable si vous voulez un faible effort de mise en oeuvre facile et la maintenabilité (et qui peuvent se permettre de la mémoire supplémentaire pour la 2ème instance).
Voir ici pour un exemple:
https://github.com/thilo20/Undo/
Je ne sais pas si cela va être d'une quelconque utilité pour vous, mais quand j'ai dû faire quelque chose de similaire sur un de mes projets, j'ai fini le téléchargement UndoEngine de http://www.undomadeeasy.com - un magnifique moteur et je n'ai vraiment pas trop s'occuper de ce qui était sous le capot, il a juste travaillé.
À mon avis, la fonction ANNULER/RÉTABLIR pourraient être mises en œuvre dans les 2 sens large.
1. Commande de Niveau (appelé niveau commande Undo/Redo)
2. Au niveau du Document (appelé global Undo/Redo)
Niveau de commandement: Comme beaucoup de réponses à souligner, c'est obtenue de manière efficace à l'aide de Memento modèle. Si la commande prend également en charge de la journalisation de l'action, un redo est facilement pris en charge.
Limitation: une Fois la portée de la commande, le undo/redo est impossible, ce qui conduit à niveau du document de(global) undo/redo
Je suppose que votre cas serait d'ajustement dans le global annuler/refaire, car il est adapté pour un modèle qui comporte beaucoup d'espace mémoire. Aussi, c'est adapté pour sélectivement annuler/refaire aussi. Il existe deux types de primitives
Dans "Toute la mémoire du Undo/Redo", l'ensemble de la mémoire est traitée comme un connecté de données (comme un arbre, ou d'une liste ou d'un graphique) et la mémoire est gérée par l'application plutôt que de l'OS. Ainsi, de nouvelles et de supprimer des opérateurs si en C++ sont surchargés pour contenir plus de structures spécifiques pour mettre en œuvre efficacement les opérations de tel qu'un. Si un nœud est modifié. b. la détention et de suppression des données etc.,
Le mode de fonctionnement est essentiellement de copier l'intégralité de la mémoire(en supposant que l'allocation de mémoire est déjà optimisé et gérées par l'application en utilisant des algorithmes avancés) et de les stocker dans une pile. Si la copie de la mémoire est demandée, la structure de l'arbre est copié basée sur le besoin d'avoir un peu profonde ou profonde copie. Une copie est faite uniquement pour la variable, qui est modifiée. Étant donné que chaque variable est allouée à l'aide d'allocation personnalisé, l'application qui a le dernier mot quand le supprimer en cas de besoin.
Les choses deviennent très intéressantes, si nous avons de la partition du Undo/Redo quand il se trouve que nous avons besoin de par programmation sélective le Undo/Redo un ensemble de l'opération. Dans ce cas, seuls ces nouvelles variables, ou supprimé des variables ou des variables modifiées sont donnés un indicateur de sorte que Undo/Redo seulement annule/rétablit ceux de la mémoire
Les choses deviennent encore plus intéressantes si nous avons besoin de faire un partiel Annuler/Refaire l'intérieur d'un objet. Quand tel est le cas, une nouvelle idée de "modèle Visiteur" est utilisé. Il est appelé "Niveau de l'Objet de Undo/redo"
1 et 2 pourrait avoir des méthodes telles que
1. BeforeUndo()
2. AfterUndo()
3. BeforeRedo()
4. AfterRedo(). Ces méthodes doivent être publiés dans la base de Commande Undo/redo ( pas la commande contextuelle), de sorte que tous les objets de mise en œuvre de ces méthodes pour obtenir des mesures précises.
Une bonne stratégie est de créer un hybride de 1 et 2. La beauté est que ces méthodes(1&2) eux-mêmes utiliser la commande modèles