Scala:对象/ UI渲染器的设计模式

时间:2018-12-19 23:34:28

标签: scala functional-programming scalaz

拥有rendererobject是一个好的设计。

我做了几次尝试,但是现在我的代码确实很丑陋。

假设我有一个类room和一个应该处理它的类。

  • 谁应该打电话给renderer.render(room)还是应该这样呢?
  • 我应该有world.renderRoom()吗?
  • 还是我应该有room.renderWith(renderer)

如何构建简单的单元测试,例如renderer.render(room, userInteraction)

那回报应该是什么?

world.renderRoom().interact()....吗?

您可以说,我不知道该怎么办哈哈。

我有几年写软件的经验,但是我正在尝试ScalaZ并试图变得更像functional-programming;这对我来说是新的。

谢谢

2 个答案:

答案 0 :(得分:2)

渲染屏幕不是纯功能,因为它具有副作用。阅读IO monad

您会发现大多数(如果不是全部)示例都在println和readln周围,但适用相同的原理。在scalaz的背景下,看看ZIO

如果正如Tim在评论中建议的那样,render是一个纯函数,那么我建议render是模型render(room)的函数,而房间状态是动作newRoomState=actions.foldLeft(oldRoomState)(state,action => someFunction(s,a))的折叠(画墙,添加沙发))SAM pattern。可以接受渲染的世界(flatMaps /减少多个渲染?!)是保证IO monad的部分。

答案 1 :(得分:2)

您似乎在询问许多不同的事情,这使得很难回答,除非以非常普通的方式。

不要将渲染代码添加到数据对象,因为这会中断关注点的分离。具有Shape方法的draw类的经典OO示例非常适合教学,但是它通过特定的绘制方式将有关形状的数据(例如边数)结合在一起。而是创建一个函数render(s: Shape),该函数使用Shape中的数据以所需的特定方式绘制(2D,3D填充,坐标列表等)。

使您的render代码起作用,以使其返回渲染的数据,而不是调用渲染库作为副作用。呈现库将需要具有功能,并返回呈现的结果,而不是直接在屏幕上绘制。

将呈现库传递给render函数(作为implicit参数),而不要使用全局对象。这使您可以使用模拟渲染器测试渲染,并且无论如何都是更灵活的设计。但是对于不同的输出设备或样式,您可能仍然需要不同的render函数。

将渲染与渲染组件的组成分开,以便您可以独立测试它们。

以功能性方式构建整个图像,然后执行一个非功能性操作以显示新图像(通过替换当前图像)。

使用用户交互功能创建具有更新的room的修改后的场景,然后重新渲染整个场景。

TL; DR

val room = Room(width, length, height)
val room3D = render(room, render3D)
val house = compose(room3d, ..., compose3D)

screen.display(house)