jeudi 24 août 2017

How to implement Memento Pattern in Kotlin

I am currently trying to implement some design patterns in Kotlin as an exercise and I'm a bit stuck with the 'Memento' pattern. My reference resource is SourceMaking: Memento.

I want to implement this structure:

Class diagram of 'Memento' Design Pattern

While following their "Checklist"

  1. Identify the roles of “caretaker” and “originator”.
  2. Create a Memento class and declare the originator a friend.
  3. Caretaker knows when to "check point" the originator.
  4. Originator creates a Memento and copies its state to that Memento.
  5. Caretaker holds on to (but cannot peek into) the Memento.
  6. Caretaker knows when to "roll back" the originator.
  7. Originator reinstates itself using the saved state in the Memento.

I can't get step 5 to work. How do I make a Memento object whose fields can be read from inside the Originator instance but that is completely opaque to the Caretaker?

I have successfully implemented this in Java as follows:

public class Originator {

    private final int id;
    private String title;
    private String description;

    public Originator(int id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Memento saveState() {
        return new Memento(new State(id, title, description));
    }

    public void restore(Memento memento) {
        id = memento.state.id;
        title = memento.state.title;
        description = memento.state.description;
    }

    private class State {
        private final int id;
        private final String title;
        private final String description;

        public State(int id, String title, String description) {
            this.id = id;
            this.title = title;
            this.description = description;
        }


    }

    public class Memento {

        private final State state;

        public Memento(State state) {
            this.state = state;
        }
    }
}

And a Caretaker

public class Caretaker {

    public Originator originator;

    public Caretaker(@NotNull Originator originator) {
        this.originator = originator;
    }

    public Originator.Memento save() {
        return originator.saveState();
    }

    public void restore(@NotNull Originator.Memento memento) {
        originator.restoreFromState(memento);
    }
}

Because they are inner classes I can read the private fields of Memento and State from my Originator instance, but to the Caretaker my Memento instance is completely opaque (only showing Objects member functions).

Now how do I implement this exact behavior in Kotlin? Basically I am missing the functionality of reading private fields of inner classes.

The closest thing I could think of was this:

class Originator(id: Long) {

    private var id: Long = id
    var description: String = ""
    var title: String = ""

    fun saveState() = Memento(State(id, title, description))

    fun restoreState(memento: Memento) {
        id = memento.state.id // <-- cannot access 'state': it is private in 'Memento'
        title = memento.state.title // <-- cannot access 'state': it is private in 'Memento'
        description = memento.state.description // <-- cannot access 'state': it is private in 'Memento'
    }

    inner class State(private val id: Long,
                  private val title: String,
                  private val description: String)

    inner class Memento(private val state: State)
}

This has the desired effect of Memento being completely opaque to my Caretaker instance, but I can't read the fields from within Originator either.
This code by the way is almost exactly the same as the generated code produced by the 'Convert Java to Kotlin' feature of IntelliJ applied to my Java code (and it obviously doesn't compile either).

So is there something obvious (or magical) I am missing here? Maybe something other than the structure displayed in the class diagram? Or can these exact specifications just not be implemented in Kotlin?

And on another note: Is the requirement of opaqueness for the Memento object actually a colloquially accepted property of the Memento Pattern or did SourceMaking come up with this requirement?

Aucun commentaire:

Enregistrer un commentaire