Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

State Preservation and Recovery Using the Memento Pattern

Tech 1

Consider a document editing application where users compose text but lack the ability to reverse accidental deletions. Without a mechansim to capture prior conditions, each modification permanently alters the working state.

public class Draft {
    private StringBuilder manuscript = new StringBuilder();
    
    public void insert(String segment) {
        manuscript.append(segment);
    }
    
    public void backspace() {
        int length = manuscript.length();
        if (length > 0) {
            manuscript.delete(length - 1, length);
        }
    }
    
    public String content() {
        return manuscript.toString();
    }
}

In this implemantation, once a character is removed via backspace(), the previous state is irretrievably lost. The Memento pattern addresses this limitation by externalizing state storage without violating encapsulation.

The Memento behavioral pattern captures and externalizes an object's internal state so that the object can be restored to this state later. This pattern involves three distinct participants: the Originator, which possesses the state to preserve; the Memento, which acts as an immutable storage container for the Originator's state; and the Caretaker, which oversees the lifecycle of stored states without accessing their contents.

To implement undo functionality, the Originator must expose methods for state serialization and deserialization. The Memento remains opaque to all objects except the Originator that created it, ensuring data integrity.

public class Draft {
    private StringBuilder manuscript = new StringBuilder();
    
    public void insert(String segment) {
        manuscript.append(segment);
    }
    
    public void backspace() {
        int length = manuscript.length();
        if (length > 0) {
            manuscript.delete(length - 1, length);
        }
    }
    
    public String content() {
        return manuscript.toString();
    }
    
    public Checkpoint save() {
        return new Checkpoint(manuscript.toString());
    }
    
    public void restore(Checkpoint checkpoint) {
        manuscript = new StringBuilder(checkpoint.getState());
    }
    
    public static class Checkpoint {
        private final String state;
        
        private Checkpoint(String state) {
            this.state = state;
        }
        
        private String getState() {
            return state;
        }
    }
}

The Caretaker maintains a history of checkpoints while remaining ignorant of their internal structure. It functions solely as a repository, pushing states onto a stack during save operations and popping them to facilitate rollbacks.

public class VersionControl {
    private java.util.Deque<Draft.Checkpoint> history = new java.util.ArrayDeque<>();
    
    public void commit(Draft draft) {
        history.push(draft.save());
    }
    
    public void rollback(Draft draft) {
        if (!history.isEmpty()) {
            draft.restore(history.pop());
        }
    }
}

Demonstrating state recovery:

public class Application {
    public static void main(String[] args) {
        Draft document = new Draft();
        VersionControl versions = new VersionControl();
        
        document.insert("Initial paragraph. ");
        document.insert("Additional content.");
        versions.commit(document);
        
        document.backspace();
        System.out.println(document.content()); // Output: Initial paragraph. Additional content
        
        versions.rollback(document);
        System.out.println(document.content()); // Output: Initial paragraph. Additional content.
    }
}

This architecture proves valuable across numerous domains. In interactive graphics software, transformations such as rotation or scaling can be captured as discrete states, allowing artists to step backward through complex editinng sequences. Gaming applications utilize this approach for save-game mechanics, preserving player position, inventory, and quest progress without exposing internal game engine structures to persistence layers. Database transaction managers implement similar rollback capabilities, maintaining snapshots before query execution to ensure atomicity. Version control systems like Git fundamentally operate on these principles, storing repository states as objects that can be checked out to reconstruct prior project snapshots.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.