
Deep Dive into Behavioral Patterns - The Memento Pattern.
Hey software designers! Today, we’re diving into the Memento pattern. This pattern is essential for capturing and restoring an object’s internal state without violating encapsulation. Let’s explore its workings, benefits, and real-world applications with detailed examples.
What is the Memento Pattern?
The Memento pattern is a behavioral design pattern that allows you to capture and restore an object’s internal state without violating encapsulation. It is useful for implementing undo and redo operations in applications.
Real-World Scenario
Imagine you’re developing a text editor that supports undo and redo functionality. Each change to the document should be reversible, allowing the user to undo or redo changes.
The Problem
When capturing and restoring an object’s state, directly accessing its internals can lead to a tightly coupled and inflexible codebase. This approach makes it difficult to implement undo and redo functionality without exposing the internal state of the object.
Without Memento Pattern
class TextEditor
  attr_accessor :text
  def initialize
    @text = ''
    @history = []
  end
  def write(text)
    @history << @text
    @text += text
  end
  def undo
    @text = @history.pop
  end
end
editor = TextEditor.new
editor.write('Hello')
editor.write(' World')
puts editor.text # Output: Hello World
editor.undo
puts editor.text # Output: Hello
Drawbacks: The history is stored within the object, leading to a tightly coupled design.
The Solution: Memento Pattern
Using the Memento pattern, we can capture and restore an object’s state without exposing its internals, promoting flexibility and scalability.
With Memento Pattern
Step 1: Define the Memento
class Memento
  attr_reader :state
  def initialize(state)
    @state = state
  end
end
Step 2: Create the Originator
class TextEditor
  attr_accessor :text
  def initialize
    @text = ''
  end
  def write(text)
    @text += text
  end
  def create_memento
    Memento.new(@text.dup)
  end
  def restore(memento)
    @text = memento.state
  end
end
Step 3: Implement the Caretaker
class Caretaker
  def initialize
    @mementos = []
  end
  def add_memento(memento)
    @mementos << memento
  end
  def get_memento
    @mementos.pop
  end
end
Step 4: Implement Client Code
editor = TextEditor.new
caretaker = Caretaker.new
editor.write('Hello')
caretaker.add_memento(editor.create_memento)
editor.write(' World')
puts editor.text # Output: Hello World
editor.restore(caretaker.get_memento)
puts editor.text # Output: Hello
Benefits: Captures and restores an object’s state without violating encapsulation, promoting flexibility and scalability.
Real-World Benefits
Scenario: Implementing Undo and Redo
Imagine you need to implement undo and redo functionality. Using the Memento pattern, you can easily capture and restore the state of the object without exposing its internals.
Without Memento Pattern:
class TextEditor
  attr_accessor :text
  def initialize
    @text = ''
    @history = []
    @redo_stack = []
  end
  def write(text)
    @history << @text
    @text += text
  end
  def undo
    @redo_stack << @text
    @text = @history.pop
  end
  def redo
    @history << @text
    @text = @redo_stack.pop
  end
end
editor = TextEditor.new
editor.write('Hello')
editor.write(' World')
puts editor.text # Output: Hello World
editor.undo
puts editor.text # Output: Hello
editor.redo
puts editor.text # Output: Hello World
Drawbacks: Tightly coupled code that is difficult to maintain and extend.
With Memento Pattern:
class Memento
  attr_reader :state
  def initialize(state)
    @state = state
  end
end
class TextEditor
  attr_accessor :text
  def initialize
    @text = ''
  end
  def write(text)
    @text += text
  end
  def create_memento
    Memento.new(@text.dup)
  end
  def restore(memento)
    @text = memento.state
  end
end
class Caretaker
  def initialize
    @undo_stack = []
    @redo_stack = []
  end
  def add_memento(memento)
    @undo_stack << memento
    @redo_stack.clear
  end
  def undo
    @redo_stack << @undo_stack.pop
  end
  def redo
    @undo_stack << @redo_stack.pop
  end
end
editor = TextEditor.new
caretaker = Caretaker.new
editor.write('Hello')
caretaker.add_memento(editor.create_memento)
editor.write(' World')
caretaker.add_memento(editor.create_memento)
puts editor.text # Output: Hello World
caretaker.undo
editor.restore(caretaker.undo)
puts editor.text # Output: Hello
caretaker.redo
editor.restore(caretaker.redo)
puts editor.text # Output: Hello World
Benefits: Clean, maintainable code with high flexibility and extensibility.
Conclusion
The Memento pattern is a powerful tool for capturing and restoring an object’s internal state without violating encapsulation. It promotes flexibility, scalability, and maintainability in your code. By using the Memento pattern, you can easily implement undo and redo functionality without tightly coupling the code. Incorporate the Memento pattern into your design strategies to build more robust and adaptable software systems.
Stay tuned for more insights into software design principles and patterns.
Thôi Lo Code Đi Kẻo Sếp nạt!!