关于实现GUI逻辑的架构技巧

sco*_*man 6 language-agnostic architecture design-patterns

所以我正在我正在这里工作的一个应用程序上实现类似svg编辑器的GUI.这些是其中需要的逻辑的一些示例:

  • 如果用户在画布上单击右键,则应创建一个新节点,后续节点应与一条线"链接",形成一个多边形
  • 如果用户点击节点上的左按钮,我应该将整个多边形集相应地移动到鼠标位置
  • 用户可以删除节点
  • 所选节点的颜色应不同
  • 用户可以通过按SHIFT并单击节点来选择多个节点

等等.

我已经实现了所有这些项目,但我不喜欢最终结果,主要是因为我不得不使用大量标志来操作状态(鼠标点击&&左键&&不移动?这样做),当然这代码可以更优雅.所以我研究了一下,然后选择了这些选项:

  • 管道模式:我将创建分别处理每个逻辑事件的类,并使用优先级顺序来提供首先要执行的操作/操作,以及事件如何传播到后续的管道项.

  • MVC:这是最常见的响应,但我现在如何使用它来使代码更干净对我来说非常模糊.

  • 状态机:这很好但是管理状态机的粒度会很复杂

所以我要求SO大师提供有关如何构建更好,更快乐的代码的技巧.

tca*_*vin 6

我建议将UI输入映射的逻辑与特定操作分离为专用对象.让我们称它们为传感器对象.不知道你的实现语言,我会对此有所了解,但你应该明白这一点.

OperationSensor
+ OnKeyDown
+ OnKeyPress
+ OnKeyUp
+ OnLeftMouseDown
+ OnLeftMouseUp
+ OnNodeSelect
+ OnNodeDeselect
+ OnDragStart
+ OnDragStop
Run Code Online (Sandbox Code Playgroud)

假设您有一个聚合所有各种UI输入的中心类UiInputManager.它使用特定于语言的机制来监听键盘和鼠标输入.它还检测基本操作,例如检测到鼠标是否被按下然后移动,这是逻辑"拖动".

UiInputManager
// event listeners
+ keyboard_keydownHandler
+ keyboard_keyupHandler
+ mouse_leftdownHandler
+ mouse_rightdownHandler
// active sensor list, can be added to or removed from
+ Sensors
Run Code Online (Sandbox Code Playgroud)

UiInputManager不负责了解这些输入导致的操作.它只是以语言特定的方式通知其传感器.

foreach sensor in Sensors
    sensor.OnDragStarted
Run Code Online (Sandbox Code Playgroud)

或者,如果传感器侦听UiInputManager发出的逻辑事件

RaiseEvent DragStarted
Run Code Online (Sandbox Code Playgroud)

你现在拥有的是将输入路由到OperationSensor子类的管道.每个OperationSensor都具有与单个操作相关的逻辑.如果它检测到操作的条件已满足,则会创建相应的Command对象并将其传回.

// Ctrl + zooms in, Ctrl - zooms out
ZoomSensor : OperationSensor

   override OnKeyDown
   {
      if keyDown.Char = '+' && keyDown.IsCtrlDepressed
         base.IssueCommand(new ZoomCommand(changeZoomBy:=10)
      elseif keyDown.Char = '-' && keyDown.IsCtrlDepressed
         base.IssueCommand(new ZoomCommand(changeZoomBy:=-10)                                 
   }
Run Code Online (Sandbox Code Playgroud)

我建议命令对象从传感器传递到UiInputManager.然后管理员可以将它们传递到您的命令处理子系统.这使管理员有机会通知传感器操作已完成,允许他们在需要时重置其内部状态.

可以通过两种不同的方式处理多步操作.您可以在SensorOperation内部实现内部状态机,也可以使用"步骤1"传感器创建"步骤2"传感器并将其添加到活动传感器列表中,甚至可能从列表中删除自身.当"步骤2"完成时,它可以重新添加"步骤1"传感器并自行移除.