2012-12-29 7 views
3

Memento Pattern에 대한 연구를하고 있는데, 필자가 경험 한 대부분의 예제가 상대적으로 비슷해 보인다. (String을 배열에 저장하고 필요할 때 복원하는 것 같다.) 지금 내가 틀렸다면 나를 바로 잡는다. 그러나 나는 방금 설명한 방법은 "Object Cloning"이라고 생각하지만 메멘토 패턴을 구현하는 다른 방법은 무엇인가?다른 방법으로 Java에서 Memento Pattern을 구현하는 방법

또한 직렬화를 사용할 수 있지만 개체의 캡슐화를 위반하고 이로 인해 메멘토 패턴을 구현할 수 없다고 말하는 사람들과 회색 영역이있는 것으로 보입니다.

그럼 누구나 패턴을 구현하는 방법에 대해 밝힐 수 있습니까? 나의 연구는 일종의 모든 다른 것들이 혼합되어 생겨 났으며 모든 것을 혼란스럽게 만들었습니다.

감사합니다 도움이 될 수 있습니다

+1

호기심에서 벗어나 그것에 관한 위키 백과 항목을 읽으셨습니까? – fge

+1

또한 다시 조사 할 때 다시 시작하는 동안 "유품 저장"을 유지 하시겠습니까? 귀하의 질문은 (나에게) 지속성에 관한 것으로 들리고, 지속성은 귀하의 필요와 환경에 따라 구현이 번창합니다. – fge

+0

예 몇 번이나 스캔했지만 대부분 책 (GoF Book & Head First Design Patterns)을 사용했습니다. 및 다양한 다른 소스. –

답변

3

자바 컬렉션 프레임 워크는 Queue을 정의합니다.

후보 코드 :

public final class Memento<T> 
{ 
    // List of saved values 
    private final Queue<T> queue = new ArrayDeque<T>(); 

    // Last entered value, whether it has been saved or not 
    private T currentValue; 

    // No initial state, ie currentValue will be null on construction, hence 
    // no constructor 

    // Set a value, don't save it 
    public void set(final T value) 
    { 
     currentValue = value; 
    } 

    // Persist the currently saved value 
    public void persist() 
    { 
     queue.add(currentValue); 
    } 

    // Return the last saved value 
    public T lastSaved() 
    { 
     return queue.element(); 
    } 

    // Return the last entered value 
    public T lastEntered() 
    { 
     return currentValue; 
    } 
} 

는 특히이 코드에서 누락은 여러 가지 있지만, 쉽게 구현 가능하다 :

  • 가 마지막으로 저장된 값으로 되돌아;
  • nulls에 대한 검사가 없습니다.
  • TSerializable을 구현하지 않습니다.
  • 편리한 방법 (예 : 값을 추가하고 마지막으로 저장된 상태로 만드십시오);
  • 코드가 스레드 안전하지 않음!

샘플 코드 :

효과에
public static void main(final String... args) 
{ 
    final Memento<String> memento = new Memento<String>(); 

    memento.set("state1"); 
    System.out.println(memento.lastEntered()); // "state1" 
    memento.persist(); 
    memento.set("state2"); 
    System.out.println(memento.lastEntered()); // "state2" 
    System.out.println(memento.lastSaved()); // "state1" 
} 

:이 향상 될 수있는 뇌사 구현은, 그러나 어떤이의 기초로 사용할 수 있습니다 - 그것은 당신에 따라 확장 needs;)

4

memento 구현에서 발생할 수있는 일반적인 문제는 종종 다른 종류의 객체의 내부 상태를 나타내는 많은 클래스가 필요하다는 것입니다. 또는 유물 구현은 객체 상태를 다른 형식 (예 : 직렬화 된 Java 객체)으로 직렬화해야합니다.

다음은 실행 취소/다시 실행 지원을 위해 상태를 캡처 할 클래스마다 특정 메모 클래스에 의존하지 않는 메모 구현에 대한 스케치입니다. 클래스 가비지 수집을 목적으로하기 때문에

public interface Reference<T> { 
    T get(); 
    void set(T value); 
} 

java.lang.ref.Reference의 추상화입니다 :

처음 소개되는 기본 개념이있다. 그러나 비즈니스 로직을 위해이를 사용해야합니다. 기본적으로 참조는 필드를 캡슐화합니다.그 사소한되지 않을 수도 있습니다 이러한 참조와 인스턴스 객체

public class Person { 
    private final Reference<String> lastName; 
    private final Reference<Date> dateOfBirth; 

    // constructor ... 

    public String getLastName() { 
     return lastName.get(); 
    } 

    public void setLastName(String lastName) { 
     this.lastName.set(lastName); 
    } 

    public Date getDateOfBirt() { 
     return dateOfBirth.get(); 
    } 

    public void setDateOfBirth(Date dateOfBirth) { 
     this.dateOfBirth.set(dateOfBirth); 
    } 
} 

주, 그러나 우리는 여기에를 떠나 : 그래서 그들은 그와 같은 기능을 수행하기위한 것입니다. 기본적으로 Change가 식별 가능한 객체의 상태에 대한 단일 식별 변화를 나타내는

public interface Caretaker { 

    void addChange(Change change); 
    void undo(); 
    void redo(); 
    void checkpoint(); 
} 

public interface Change { 

    Change createReversal(); 
    void revert(); 
} 

:

지금 여기 메멘토 구현을위한 세부 사항입니다. Changerevert 메서드를 호출하여 되돌릴 수 있으며 createReversal 메서드로 만든 변경 내용을 되돌리면 되돌릴 수 있습니다. CaretakeraddChange 메서드를 통해 개체 상태에 대한 변경 사항을 누적합니다. undoredo 메소드를 호출하여 Caretaker은 다음 체크 포인트에 도달 할 때까지 모든 변경 사항을 되돌 리거나 다시 실행 (즉, 변경 취소 되돌리기)합니다. 검사 점은 모든 관찰 된 변경 사항이 모든 변경된 객체의 모든 상태를 유효한 하나의 유효한 구성으로 변환하는 변경으로 축적되는 지점을 나타냅니다. 체크 포인트는 대개 작업 전후에 생성됩니다. 이들은 checkpoint 메소드를 통해 생성됩니다.

public class ReferenceChange<T> implements Change { 

    private final Reference<T> reference; 
    private final T oldValue; 
    private final T currentReferenceValue; 

    public ReferenceChange(Reference<T> reference, T oldValue, 
      T currentReferenceValue) { 
     super(); 
     this.reference = reference; 
     this.oldValue = oldValue; 
     this.currentReferenceValue = currentReferenceValue; 
    } 

    @Override 
    public void revert() { 
     reference.set(oldValue); 
    } 

    @Override 
    public Change createReversal() { 
     return new ReferenceChange<T>(reference, currentReferenceValue, 
       oldValue); 
    } 
} 

public class CaretakingReference<T> implements Reference<T> { 

    private final Reference<T> delegate; 
    private final Caretaker caretaker; 

    public CaretakingReference(Reference<T> delegate, Caretaker caretaker) { 
     super(); 
     this.delegate = delegate; 
     this.caretaker = caretaker; 
    } 

    @Override 
    public T get() { 
     return delegate.get(); 
    } 

    @Override 
    public void set(T value) { 
     T oldValue = delegate.get(); 
     delegate.set(value); 
     caretaker.addChange(new ReferenceChange<T>(delegate, oldValue, value)); 
    } 
} 

Reference의 값이 어떻게 변경되었는지 나타내는 Change이 존재 : 지금 여기

그리고는 Reference으로 Caretaker 활용하는 방법이다. 이 ChangeCaretakingReference이 설정 될 때 만들어집니다. 이 구현에서는 구현 내에서 중첩 된 Reference이 필요합니다. 의 revertCaretakingReference을 통해 새 addChange을 트리거하지 않아야하기 때문입니다.

컬렉션 속성은 Reference을 사용할 필요가 없습니다. 이 경우에는 보전을 실행하는 사용자 지정 구현을 사용해야합니다. Primitives는 autoboxing과 함께 사용할 수 있습니다.

이 구현은 필드 대신 직접 참조를 사용하여 추가 런타임 및 메모리 비용을 추론합니다.