Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Design patterns 实现GUI逻辑的架构技巧_Design Patterns_Language Agnostic_Architecture - Fatal编程技术网

Design patterns 实现GUI逻辑的架构技巧

Design patterns 实现GUI逻辑的架构技巧,design-patterns,language-agnostic,architecture,Design Patterns,Language Agnostic,Architecture,所以我在这里工作的一个应用程序上实现了一个类似于GUI的svg编辑器。以下是一些需要的逻辑示例: 如果用户单击画布上的右按钮,则应创建一个新节点,随后的节点应与一条线“链接”,形成一个多边形 如果用户用左键单击某个节点,我应该将整个多边形集相应地移动到鼠标位置 用户可以删除节点 所选节点的颜色应不同 用户可以通过按SHIFT键并单击节点来选择多个节点 等等 我已经实现了所有这些项目,但我不喜欢最终的结果,主要是因为我必须使用许多标志来操纵状态(鼠标单击和左键-&不移动?执行此操作),而且此代

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

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

我已经实现了所有这些项目,但我不喜欢最终的结果,主要是因为我必须使用许多标志来操纵状态(鼠标单击和左键-&不移动?执行此操作),而且此代码肯定会更优雅。所以我做了一些研究,得出了以下选择:

  • 管道模式:我将创建单独处理每个逻辑事件的类,并使用优先级顺序来提供要做什么/首先要处理什么,以及事件将如何传播到后续管道项目

  • MVC:这是最常见的回答,但目前我对如何使用它使代码更干净还很模糊

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


因此,我向S.O.大师请教如何构建更好、更快乐的代码的技巧。

Martin Fowler对MVC和相关模式有深入的研究。此外,您可能还想看看如何让UI元素了解它们的行为(即,当单击某个节点时应该移动或删除该节点等)。

对于UIs MVC,现在已经非常普遍了。简单地说,M(模型)包含状态,V(视图)显示可视元素,C(控制器)发送入站用户操作,如鼠标单击。目标是模型不直接关心视图,除了触发事件


我可能会在模型中加入“智能”。模型将知道何时选择节点、其相邻节点形成多边形、状态机等。此设计为您提供了几个好处。它独立于UI呈现的细节;因此,您可以在不中断核心功能的情况下进行主要视图更改。它还使单元测试更容易。

我建议将UI输入映射到特定操作的逻辑分离到专用对象中。让我们称之为传感器对象。由于不知道您的实现语言,我将对此进行泛化,但您应该了解这一点

OperationSensor
+ OnKeyDown
+ OnKeyPress
+ OnKeyUp
+ OnLeftMouseDown
+ OnLeftMouseUp
+ OnNodeSelect
+ OnNodeDeselect
+ OnDragStart
+ OnDragStop
假设您有一个聚合所有各种UI输入的中心类,
UiInputManager
。它使用特定于语言的机制来监听键盘和鼠标输入。它还检测基本操作,例如检测鼠标是否按下然后移动,这是一种逻辑“拖动”

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

foreach sensor in Sensors
    sensor.OnDragStarted
或者,如果传感器侦听UiInputManager发出的逻辑事件

RaiseEvent DragStarted
您现在拥有的是将输入路由到OperationSensor子类的管道。每个操作传感器的逻辑仅与单个操作有关。如果它检测到操作的条件已满足,则会创建相应的命令对象并将其传递回

// 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)                                 
   }
我建议将命令对象从传感器传递到UiInputManager。然后,管理器可以将它们传递到命令处理子系统中。这使管理者有机会通知传感器操作已完成,允许传感器在需要时重置其内部状态


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

节目开始有点晚了,但我想补充一点,这方面的常见模式是中介模式,您可以将不同节点之间交互的复杂性移动到一个单独的类,中介(比如ConnectionCreator类)。请参见Gamma及其同事的designpatterns:

是的,我已经在使用命令模式来实现操作执行。我现在更专注于修复逻辑混乱。你是说他应该像他在问题中所说的那样使用管道?我是在建议一些新的东西。(我想)他说他希望使用一条管道来捕获和引导事件,然后将其链接到后续事件处理程序中,直到hanlder处理完事件为止。我专注于正在执行的操作,而不是触发它们的事件。虽然如果OP澄清了他的皮普莱恩想法,我可以进行更多的比较/对比。我期待着OP的回复,看看他对这件事的看法。事实上,我一直在想,这件事解释得很好!很高兴看到我正朝着正确的方向看:)。我的想法中有一点让我想到了管道模式,那就是关于操作优先级,以及当某些命令无法明确地将事件传播到整个链中时,需要“中断”传感器通知。这可以通过使用具有优先级的集合(而不是列表)轻松实现,并使传感器事件返回一些代码标志,或者甚至使用布尔值作为返回值来指示流程继续或中断。谢谢顺便说一句:在较小的传感器中打破多步骤操作的好主意。这使得状态机的使用更加简单和清晰。
// 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)                                 
   }