메멘토 패턴은 행동 디자인 패턴입니다. 이 패턴은 객체 상태의 스냅숏을 만든 후 나중에 복원할 수 있도록 합니다.
메멘토는 함께 작동하는 객체의 내부 구조와 스냅숏들 내부에 보관된 데이터를 손상하지 않습니다.
복잡도:
인기도:
사용 사례들: 메멘토의 원칙은 직렬화를 사용하여 달성할 수 있으며, 이는 루비 코드에서 매우 일반적입니다. 직렬화는 객체 상태의 스냅숏을 만드는 유일한 또는 가장 효율적인 방법은 아니나 다른 객체로부터 오리지네이터의 구조를 보호하면서 상태 백업을 저장할 수 있도록 합니다.
개념적인 예시
이 예시는 메멘토 패턴의 구조를 보여주고 다음 질문에 중점을 둡니다:
패턴은 어떤 클래스들로 구성되어 있나요?
이 클래스들은 어떤 역할을 하나요?
패턴의 요소들은 어떻게 서로 연관되어 있나요?
main.rb: 개념적인 예시
# 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: 실행 결과
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
다른 언어로 작성된 메멘토