我正在用Java创建一个简单的记事本应用程序,并尝试使用流畅且不可变的样式,以便尝试它。我发现它非常有趣,并且看到了很多关于函数式编程(代码清晰度等)的优点。
但我总体上遇到了事件处理问题。我有一个类TextArea,提供类似于你期望的类似记事本的文档。然后我有另一个类ScrollBar。它们由一个主类CentralController操纵,它使滚动条和文本区域保持一致(不是真正的类名,它只是用于这个例子)。
因此,如果用户按下向下箭头,CentralController只需调用TextArea.withDownArrow()并返回TextArea的新副本,并将光标向下移动。问题是现在需要移动Scroll Bar,因此CentralController需要知道TextArea是否被该向下箭头滚动。
我第一次尝试解决它是返回一个不仅包含新TextArea的对象,还包含一个标记,指出滚动是否需要更新。这样做效果不错,但感觉不对,因为我不再回归TextArea了,而你真的应该在适当的时候。函数式编程(粗略地说)。
然后我尝试在TextArea中设置一个标志,如果TextArea.withDownArrow()导致滚动,它将被设置。这也很有效但似乎错误地将方法结果存储在全球范围内'在课堂里。它还存在两次调用withDownArrow()然后用新结果覆盖该标志的问题。
我已经阅读了一些关于反应式编程的内容,它确实看起来很有趣,但我不确定它是否适用于这种情况,即你有一个孩子'类向同胞发送消息。
只是补充一点,我认为正常的事件处理在这种情况下不会起作用。使用不可变对象更改内容时,可以创建新对象。因此,任何尝试向侦听器发送事件的对象都将发送到旧指针。
我错过了一个明显的方法,因为我觉得我是这样的吗?或者是否可以使用普通的Java事件处理技术并且我什么都不担心?
编辑:我想我现在已经找到了一个足够好的解决方案。即使接收事件的类(ScrollBar)始终被重新创建,该类的成员也不会被重新创建。只有变化的东西。
所以我在ScrollBar中只有一个简单的事件接收器方法,而TextArea可以有一个监听器列表(基本上是'正常'与侦听器一起做事件的方式)。
总之,我的错误认为我需要将事件发送到实例,而不是该实例的成员。
答案 0 :(得分:0)
您必须区分值对象和逻辑对象。值对象只包含值,没有逻辑(*)。它们可以是不可变的。
但是当然文本区域不能是值对象,滚动条也不能是值对象,因为它们必须包含逻辑。它们也不可能是不可改变的,因为它们包含国家。所以,抓住这一切。它不起作用。
(*)或者至少没有处理外部实体或操纵任何自己状态的逻辑。