Donnez-moi quelques idées sur la façon d'implémenter la fonctionnalité undo/redo - comme nous avons dans les éditeurs de texte. Quels algorithmes devrais-je utiliser et ce que je pourrais lire. Merci.
Réponses
Trop de publicités?Une façon de mettre en œuvre une fonction de base d'annulation/rétablissement est d'utiliser à la fois les modèles de conception memento et commande.
Memento vise à conserver l'état d'un objet pour le restaurer ultérieurement par exemple. Ce mémento doit être le plus petit possible dans un but d'optimisation.
El commande encapsule dans un objet (une commande) des instructions à exécuter au moment voulu.
Sur la base de ces deux concepts, vous pouvez écrire un historique d'annulation/rétablissement de base, comme celui qui suit, codé en TypeScript ( extrait et adapté de la bibliothèque frontale Interacto ).
Une telle histoire repose sur deux piles :
- la pile des objets qui peuvent être annulés
- la pile pour les objets qui peuvent être refaits
Les commentaires sont fournis dans l'algorithme. Notez simplement que lors d'une opération d'annulation, la pile de redo doit être effacée ! La raison est de laisser l'application dans un état stable : si vous revenez dans le passé pour refaire certaines actions que vous avez faites, vos anciennes actions n'existeront plus lorsque vous modifierez le futur.
export class UndoHistory {
/** The undoable objects. */
private readonly undos: Array<Undoable>;
/** The redoable objects. */
private readonly redos: Array<Undoable>;
/** The maximal number of undo. */
private sizeMax: number;
public constructor() {
this.sizeMax = 0;
this.undos = [];
this.redos = [];
this.sizeMax = 30;
}
/** Adds an undoable object to the collector. */
public add(undoable: Undoable): void {
if (this.sizeMax > 0) {
// Cleaning the oldest undoable object
if (this.undos.length === this.sizeMax) {
this.undos.shift();
}
this.undos.push(undoable);
// You must clear the redo stack!
this.clearRedo();
}
}
private clearRedo(): void {
if (this.redos.length > 0) {
this.redos.length = 0;
}
}
/** Undoes the last undoable object. */
public undo(): void {
const undoable = this.undos.pop();
if (undoable !== undefined) {
undoable.undo();
this.redos.push(undoable);
}
}
/** Redoes the last undoable object. */
public redo(): void {
const undoable = this.redos.pop();
if (undoable !== undefined) {
undoable.redo();
this.undos.push(undoable);
}
}
}
El Undoable
est assez simple :
export interface Undoable {
/** Undoes the command */
undo(): void;
/** Redoes the undone command */
redo(): void;
}
Vous pouvez désormais écrire des commandes annulables qui agissent sur votre application.
Par exemple (toujours basé sur les exemples d'Interacto), vous pouvez écrire une commande comme celle-ci :
export class ClearTextCmd implements Undoable {
// The memento that saves the previous state of the text data
private memento: string;
public constructor(private text: TextData) {}
// Executes the command
public execute() void {
// Creating the memento
this.memento = this.text.text;
// Applying the changes (in many
// cases do and redo are similar, but the memento creation)
redo();
}
public undo(): void {
this.text.text = this.memento;
}
public redo(): void {
this.text.text = '';
}
}
Vous pouvez maintenant exécuter et ajouter la commande à l'instance UndoHistory :
const cmd = new ClearTextCmd(...);
//...
undoHistory.add(cmd);
Enfin, vous pouvez lier un bouton d'annulation (ou un raccourci) à cet historique (même chose pour l'annulation).
De tels exemples sont détaillés sur la page Page de documentation d'Interacto .
Edit :
Notez qu'il existe plusieurs algorithmes d'annulation et de rétablissement. J'ai détaillé l'algorithme linéaire classique. Pour les éditeurs de code, les chercheurs ont proposé l'"algorithme d'annulation sélective". (article de recherche) . Les algorithmes d'annulation et de rétablissement pour l'édition collaborative sont également spécifiques. (article de recherche) .
El Motif Memento a été conçu pour cela.
Avant de l'implémenter vous-même, notez que c'est assez commun, et que le code existe déjà - Par exemple, si vous codez en .Net, vous pouvez utiliser IEditableObject et dans le monde javascript, vous pouvez utiliser des bibliothèques immuables telles que immer.js .
Si les actions sont réversibles, par exemple : ajouter 1, faire bouger un joueur, etc. comment utiliser le modèle de commande pour mettre en œuvre la fonction Annuler/Refaire. . Suivez le lien et vous trouverez des exemples détaillés sur la manière de procéder.
Sinon, utilisez État sauvegardé comme expliqué par @Lazer.
Vous pouvez étudier un exemple de cadre d'annulation et de rétablissement existant, le premier résultat de Google est le suivant codeplex (pour .NET) . Je ne sais pas si c'est mieux ou pire qu'un autre cadre, il y en a beaucoup.
Si votre objectif est de disposer d'une fonctionnalité annuler/refaire dans votre application, vous pouvez tout aussi bien choisir un framework existant qui semble adapté à votre type d'application.
Si vous souhaitez apprendre à construire votre propre système d'annulation et de rétablissement, vous pouvez télécharger le code source et jeter un coup d'œil aux deux modèles et aux détails de l'installation.
- Réponses précédentes
- Plus de réponses
3 votes
Vous pouvez peut-être ajouter quelques détails sur le domaine dans lequel travaille votre logiciel (traitement de texte ? graphique ? base de données ?) et éventuellement les plates-formes / langages de programmation.