Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Advanced Java Design Patterns: Memento and State Patterns

Tech Oct 5 17

In the previous article, we explored the Strategy and Template patterns within behavioral design patterns. This article will delve into two additional behavioral patterns: the Memento Pattern and the State Pattern.

Memento Pattern

Overview

The Memento Pattern is used to capture and externalize an object's internal state without violating encapsulation, sothat the object can be restored to this state later. This pattern falls under the category of behavioral patterns.

Its primary purpose is to preserve an object's state in a way that allows it to be restored at a later time, while maintaining encapsulation.

Common applications include document saving (like in text editors), game saving systems, system backups, and undo mechanisms such as Ctrl+Z.

Components

The Memento Pattern consists of three key components:

  • Memento: Stores the internal state of the originator.
  • Originator: Creates and restores its state using mementos.
  • Caretaker: Responsible for storing mementos but cannot inspect their contents.

Example Implementation

Let's consider a gaming scenario where players can save and load their progress.

First, define the Memento class that holds game state:

class GameState {
    private int level;
    private int health;
    
    public GameState(int level, int health) {
        this.level = level;
        this.health = health;
    }
    
    public int getLevel() { return level; }
    public void setLevel(int level) { this.level = level; }
    public int getHealth() { return health; }
    public void setHealth(int health) { this.health = health; }
}

Next, implement the Originator (the player):

class Player {
    private int level;
    private int health;
    
    public Player(int level, int health) {
        this.level = level;
        this.health = health;
    }
    
    public GameState saveState() {
        return new GameState(level, health);
    }
    
    public void restoreState(GameState state) {
        this.level = state.getLevel();
        this.health = state.getHealth();
    }
    
    public void displayStatus() {
        System.out.println("Player Status - Level: " + level + ", Health: " + health);
    }
    
    public void levelUp() {
        this.level++;
        this.health += 10;
        System.out.println("Player leveled up! Level increased by 1, Health increased by 10.");
    }
    
    public boolean canChallengeBoss() {
        return this.level > 2 && this.health > 100;
    }
}

Finally, create the Caretaker (save manager):

class SaveManager {
    private GameState savedState;
    
    public GameState getSavedState() {
        return savedState;
    }
    
    public void setSavedState(GameState state) {
        this.savedState = state;
    }
}

Testing implementation:

public static void main(String[] args) {
    int initialLevel = 1;
    int initialHealth = 100;
    
    Player player = new Player(initialLevel, initialHealth);
    System.out.println("Player enters game!");
    player.displayStatus();
    
    player.levelUp();
    player.displayStatus();
    
    SaveManager saveManager = new SaveManager();
    System.out.println("Saving game state...");
    saveManager.setSavedState(player.saveState());
    System.out.println("Game saved successfully!");
    
    System.out.println("Attempting to challenge boss...");
    if (player.canChallengeBoss()) {
        System.out.println("Boss defeated!");
        return;
    }
    
    System.out.println("Boss defeat! Loading saved game...");
    player.restoreState(saveManager.getSavedState());
    System.out.println("Game loaded successfully!");
    
    player.levelUp();
    player.displayStatus();
    
    System.out.println("Attempting to challenge boss again...");
    if (player.canChallengeBoss()) {
        System.out.println("Boss defeated!");
    }
}

Advantages

  • Provides a mechanism to restore object states
  • Encapsulates state information

Disadvantages

  • Resource intensive
  • Requires knowledge of all possible states

Use Cases

  • Scenarios requiring state persistence and restoration

State Pattern

Overview

The State Pattern is a behavioral design pattern that enables an object to alter its behavior when its internal state changes. It's particularly useful when complex conditional logic governs an object's behavior.

This pattern moves state-dependent logic out of the main class into separate state classes, simplifying the main class's responsibilities.

Components

  • Context: Maintains a reference to the current state object
  • State Interface: Defines methods for handling state-specific behavior
  • Concrete States: Implement the state interface with specific behaviors

Example Implementation

Consider a music player with play/pause functionality:

interface PlayerState {
    void press();
}

class PlayingState implements PlayerState {
    @Override
    public void press() {
        System.out.println("Music playing...");
    }
}

class PausedState implements PlayerState {
    @Override
    public void press() {
        System.out.println("Music paused.");
    }
}

class MusicPlayer {
    private PlayerState currentState;
    private int toggleCount = 0;
    
    public MusicPlayer(PlayerState initialState) {
        this.currentState = initialState;
    }
    
    public void pressButton() {
        if ((toggleCount & 1) == 0) {
            currentState = new PlayingState();
        } else {
            currentState = new PausedState();
        }
        currentState.press();
        toggleCount++;
    }
}

Testing the implementation:

public static void main(String[] args) {
    MusicPlayer player = new MusicPlayer(new PlayingState());
    player.pressButton(); // Play
    player.pressButton(); // Pause
    player.pressButton(); // Play
}

Advantages

  • Easy to extend with new states
  • Improves code organization

Disadvantages

  • Increases number of classes
  • Complexity in state transitions

Use Cases

  • Objects whose behavior changes based on internal state
  • Complex conditional logic replacement

Comparison with Strategy Pattern

While both patterns appear similar, they differ in key aspects:

  • State Pattern: States are mutually exclusive and cannot be interchanged
  • Strategy Pattern: Strategies are interchangeable
  • State changes are managed internally
  • Strategy changes are typically initiated by the client

Conclusion

Both patterns provide elegant solutions for managing object behavior and state. The Memento Pattern excels in scenarios requiring state persistence, while the State Pattern shines in situations where object behavior varies based on its internal state.

Tags: Java

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.