2017-08-24 49 views
0

我目前正在尝试在Kotlin中实现一些设计模式作为练习,并且我有点卡住了'Memento'模式。我的参考资源是SourceMaking: Memento如何在Kotlin中实现Memento模式

我想要实现这样的结构:

Class diagram of 'Memento' Design Pattern

虽然跟随他们的 “清单”

  1. 确定“看守”和“鼻祖”的角色。
  2. 创建纪念品课程并将创建者声明为朋友。
  3. 看守人知道什么时候“检查点”发端人。
  4. 发起人创建一个纪念品并将其状态复制到该纪念品。
  5. 看守持有(但不能偷看)纪念品。
  6. 看守人知道何时“回滚”发起人。
  7. 发起人使用Memento中保存的状态恢复自己。

我无法获得第5步的工作。如何制作一个Memento对象,其字段可以从Originator实例中读取,但对Caretaker完全不透明?

我已经成功地在Java中实现这个如下:

public class Originator { 

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

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

    /* skipping title and description getter & setter */ 

    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; 
     } 
    } 
} 

而且看守

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); 
    } 
} 

因为他们是内部类,我可以从我的Originator例如读取MementoState私人领域,但是对于Caretaker我的Memento实例是完全不透明的(仅显示Object的成员函数)。

现在我该如何在Kotlin中实现这种确切的行为?基本上我缺少阅读内部类的私人领域的功能。

我能想到的最接近的事是这样的:

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) 
} 

这有Memento是完全不透明的,以我的Caretaker实例的预期效果,但我不能读Originator中的字段无论是。
这段代码顺便说一下,与我的Java代码中使用IntelliJ的'Convert Java to Kotlin'特性生成的生成代码几乎完全一样(它显然不能编译)。

那么有什么明显的(或神奇的)我在这里失踪?也许是类图中显示的结构以外的东西?或者,这些确切的规格是不是可以在Kotlin中实现?

另一个说明:Memento对象的不透明度要求实际上是Memento Pattern的通俗接受属性还是SourceMaking提出了这个要求?

回答

1

您可以定义Memento公共父类和私有继承类吧:

class Originator { 
    /* irrelevant declarations skipped */ 

    abstract inner class Memento 

    private inner class MementoImpl(val state: State) : Memento() 

    fun saveState(): Memento { 
     return MementoImpl(State(id, title, description)) 
    } 

    fun restore(memento: Memento) { 
     memento as MementoImpl 
     id = memento.state.id 
     title = memento.state.title 
     description = memento.state.description 
    } 
} 

实现类是private,并且,外Originator的情况下,才会被视为Memento(见功能签名),以便该状态不可访问。

+0

不错!没有想到这一点。你如何评价解决方案?这是应该怎么做,你认为,或者有可能是一种不同的方式,从Java示例中删除更多,但也许更好的Kotlin代码?我想说的是,在代码审查中,你会赞成还是会对这个空的界面持怀疑态度? –

+0

我最初的怀疑主义可能源自这样一个事实,即我喜欢将接口看作是描述什么是*做什么*而不是什么* *的东西。在这种情况下,它主要用于破解类型系统,不是吗? 没有试图成为一个自作聪明在这里。这是一个实用的解决方案,效果很好,我可能会在生产中使用它。但是由于我仍然在学习和练习,以便更好地理解语言,所以我只想知道,如果你认为这是“kotlin的方式”,以您的意见来做 –

+1

我宁可同意这是一种黑客攻击可见性系统不允许某个类的成员对某些类是可见的(除了子类 - 这里有'受保护的',这里虽然没用),但在Kotlin中,这个模式并没有“朋友”的可见性。在Java中,封闭类可以访问私有成员,这对于这种模式已经足够了。实际上,我在界面上看到的一个更大的问题是它可以由不同的类来实现。我已经编辑了答案,现在它是一个'抽象的内部类',所以它不能被超越'Originator'范围继承。 – hotkey

0

您应该使用包级访问来定义您的Memento类属性。

+2

Kotlin没有包级别的访问权限。只有内部用于模块级访问。 – hotkey

+0

然后,他应该使用'内部'我想...... – alirabiee

+0

我不认为'内部'是这样做的正确方法...并且都不会像在Java中那样进行包私人访问。因为那样我就必须根据我的现场可视性需求来组织我的软件包,甚至更糟糕的是,模块结构。这对我来说似乎更像是一个黑客...... –

相关问题