拥有renderer
和object
是一个好的设计。
我做了几次尝试,但是现在我的代码确实很丑陋。
假设我有一个类room
和一个应该处理它的类。
renderer.render(room)
还是应该这样呢?world.renderRoom()
吗? room.renderWith(renderer)
? 如何构建简单的单元测试,例如renderer.render(room, userInteraction)
那回报应该是什么?
world.renderRoom().interact()....
吗?
您可以说,我不知道该怎么办哈哈。
我有几年写软件的经验,但是我正在尝试ScalaZ
并试图变得更像functional-programming
;这对我来说是新的。
谢谢
答案 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)