Pamiątka to behawioralny wzorzec projektowy umożliwiający zapisywanie “migawek” stanu obiektu i późniejsze jego przywracanie.
Wzorzec Pamiątka nie wpływa na wewnętrzną strukturę obiektu z którym współpracuje, ani na dane przechowywane w migawkach.
Złożoność:
Popularność:
Przykłady użycia: Zasada działania Pamiątki opiera się na serializacji, która jest dość powszechnie stosowana w Ruby. Nie jest to jedyny, czy najefektywniejszy sposób zapisywania migawki stanu obiektu, ale pozwala na przechowywanie kopii zapasowych, chroniąc jednocześnie strukturę obiektu źródłowego przed innymi obiektami.
Przykład koncepcyjny
Poniższy przykład ilustruje strukturę wzorca Pamiątka ze szczególnym naciskiem na następujące kwestie:
Z jakich składa się klas?
Jakie role pełnią te klasy?
W jaki sposób elementy wzorca są ze sobą powiązane?
main.rb: Przykład koncepcyjny
# The Originator holds some important state that may change over time. It also
# defines a method for saving the state inside a memento and another method for
# restoring the state from it.
class Originator
# For the sake of simplicity, the originator's state is stored inside a single
# variable.
attr_accessor :state
private :state
# @param [String] state
def initialize(state)
@state = state
puts "Originator: My initial state is: #{@state}"
end
# The Originator's business logic may affect its internal state. Therefore,
# the client should backup the state before launching methods of the business
# logic via the save() method.
def do_something
puts 'Originator: I\'m doing something important.'
@state = generate_random_string(30)
puts "Originator: and my state has changed to: #{@state}"
end
private def generate_random_string(length = 10)
ascii_letters = [*'a'..'z', *'A'..'Z']
(0...length).map { ascii_letters.sample }.join
end
# Saves the current state inside a memento.
def save
ConcreteMemento.new(@state)
end
# Restores the Originator's state from a memento object.
def restore(memento)
@state = memento.state
puts "Originator: My state has changed to: #{@state}"
end
end
# The Memento interface provides a way to retrieve the memento's metadata, such
# as creation date or name. However, it doesn't expose the Originator's state.
class Memento
# @abstract
#
# @return [String]
def name
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
# @abstract
#
# @return [String]
def date
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
end
class ConcreteMemento < Memento
# @param [String] state
def initialize(state)
@state = state
@date = Time.now.strftime('%F %T')
end
# The Originator uses this method when restoring its state.
attr_reader :state
# The rest of the methods are used by the Caretaker to display metadata.
def name
"#{@date} / (#{@state[0, 9]}...)"
end
# @return [String]
attr_reader :date
end
# The Caretaker doesn't depend on the Concrete Memento class. Therefore, it
# doesn't have access to the originator's state, stored inside the memento. It
# works with all mementos via the base Memento interface.
class Caretaker
# @param [Originator] originator
def initialize(originator)
@mementos = []
@originator = originator
end
def backup
puts "\nCaretaker: Saving Originator's state..."
@mementos << @originator.save
end
def undo
return if @mementos.empty?
memento = @mementos.pop
puts "Caretaker: Restoring state to: #{memento.name}"
begin
@originator.restore(memento)
rescue StandardError
undo
end
end
def show_history
puts 'Caretaker: Here\'s the list of mementos:'
@mementos.each { |memento| puts memento.name }
end
end
originator = Originator.new('Super-duper-super-puper-super.')
caretaker = Caretaker.new(originator)
caretaker.backup
originator.do_something
caretaker.backup
originator.do_something
caretaker.backup
originator.do_something
puts "\n"
caretaker.show_history
puts "\nClient: Now, let's rollback!\n"
caretaker.undo
puts "\nClient: Once more!\n"
caretaker.undo
output.txt: Wynik działania
Originator: My initial state is: Super-duper-super-puper-super.
Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: CHYzYSIWbqvWkCzIHOqTyEJWfQlFMn
Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: vbkhwCeAEQBpLwQLlhmpcvUnwzxVnT
Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: SBWlQnAEPLsitiOQAZbGlXHZAeWBoW
Caretaker: Here's the list of mementos:
2023-08-11 15:02:35 / (Super-dup...)
2023-08-11 15:02:35 / (CHYzYSIWb...)
2023-08-11 15:02:35 / (vbkhwCeAE...)
Client: Now, let's rollback!
Caretaker: Restoring state to: 2023-08-11 15:02:35 / (vbkhwCeAE...)
Originator: My state has changed to: vbkhwCeAEQBpLwQLlhmpcvUnwzxVnT
Client: Once more!
Caretaker: Restoring state to: 2023-08-11 15:02:35 / (CHYzYSIWb...)
Originator: My state has changed to: CHYzYSIWbqvWkCzIHOqTyEJWfQlFMn
Pamiątka w innych językach