Scala:UI 对象/渲染器的设计模式
Scala: Design pattern for object / renderer for UI
如果有 renderer
和 object
会是一个好的设计。
我已经做了几次尝试,但我的代码现在真的很难看。
假设我有一个 class room
和一个 class 应该处理渲染它。
- 谁应该给
renderer.render(room)
打电话,或者应该这样吗?
- 我应该
world.renderRoom()
吗?
- 或者我应该
room.renderWith(renderer)
吗?
如何构建简单的单元测试,例如 renderer.render(room, userInteraction)
return 应该是什么?
world.renderRoom().interact()....
?
如你所见,我不知道该怎么办哈哈。
我有几年编写软件的经验,但我正在尝试 ScalaZ
并努力变得更像 functional-programming
;这对我来说是新的。
谢谢
渲染屏幕不是一个纯粹的函数,因为它有副作用。阅读 IO monad。
您会发现大多数(如果不是全部)示例都围绕 println 和 readln,但适用相同的原则。在 scalaz 的上下文中,请查看 ZIO.
如果 Tim 在评论中建议,渲染是一个纯函数,那么我建议渲染是模型渲染(房间)的函数,房间状态是动作的折叠 newRoomState=actions.foldLeft(oldRoomState)(state,action => someFunction(s,a))
(油漆墙,加沙发什么的)见SAM pattern。接受渲染的世界(flatMaps/reduce 超过多个渲染?!)是保证 IO monad 的部分)
你似乎问了很多不同的事情,这使得除了非常笼统的方式外很难回答。
不要向数据对象添加呈现代码,因为这会破坏关注点分离。 classic OO 示例 Shape
class 和 draw
方法非常适合教学,但它将有关形状(例如边数)的数据与具体绘制方法。相反,创建一个函数 render(s: Shape)
,它使用 Shape
中的数据以您想要的特定方式绘制(2D、3D 填充、坐标列表等)。
使您的 render
代码具有功能性,以便它 return 渲染数据而不是调用渲染库作为副作用。渲染库需要具有功能性,并且 return 渲染结果而不是直接绘制到屏幕上。
将渲染库传递给 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)
如果有 renderer
和 object
会是一个好的设计。
我已经做了几次尝试,但我的代码现在真的很难看。
假设我有一个 class room
和一个 class 应该处理渲染它。
- 谁应该给
renderer.render(room)
打电话,或者应该这样吗? - 我应该
world.renderRoom()
吗? - 或者我应该
room.renderWith(renderer)
吗?
如何构建简单的单元测试,例如 renderer.render(room, userInteraction)
return 应该是什么?
world.renderRoom().interact()....
?
如你所见,我不知道该怎么办哈哈。
我有几年编写软件的经验,但我正在尝试 ScalaZ
并努力变得更像 functional-programming
;这对我来说是新的。
谢谢
渲染屏幕不是一个纯粹的函数,因为它有副作用。阅读 IO monad。
您会发现大多数(如果不是全部)示例都围绕 println 和 readln,但适用相同的原则。在 scalaz 的上下文中,请查看 ZIO.
如果 Tim 在评论中建议,渲染是一个纯函数,那么我建议渲染是模型渲染(房间)的函数,房间状态是动作的折叠 newRoomState=actions.foldLeft(oldRoomState)(state,action => someFunction(s,a))
(油漆墙,加沙发什么的)见SAM pattern。接受渲染的世界(flatMaps/reduce 超过多个渲染?!)是保证 IO monad 的部分)
你似乎问了很多不同的事情,这使得除了非常笼统的方式外很难回答。
不要向数据对象添加呈现代码,因为这会破坏关注点分离。 classic OO 示例 Shape
class 和 draw
方法非常适合教学,但它将有关形状(例如边数)的数据与具体绘制方法。相反,创建一个函数 render(s: Shape)
,它使用 Shape
中的数据以您想要的特定方式绘制(2D、3D 填充、坐标列表等)。
使您的 render
代码具有功能性,以便它 return 渲染数据而不是调用渲染库作为副作用。渲染库需要具有功能性,并且 return 渲染结果而不是直接绘制到屏幕上。
将渲染库传递给 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)