【问题标题】:Design Pattern for separating a model from its graphical representation用于将模型与其图形表示分离的设计模式
【发布时间】:2013-08-28 18:08:22
【问题描述】:

问题

我正在寻找(希望)一种我可能不知道的设计模式,它可以帮助完成我在下面定义的问题的一些代码分离:

我有一组代表各种实体的类,例如 PointVectorArc 等,它们属于二维笛卡尔平面,由类 Model 表示。 Model 类充当这些实体的集合并存储有用的计算值。

我还有一个可视化界面,应该渲染模型状态的 2D 图像,并且还可以通过向模型添加实体来与模型进行小范围的交互(例如,用户可以添加 @987654326 @ 通过单击某处的 GUI 来添加模型。)。

第一个想法

起初,我有一个接口Drawable,它有一个方法Draw,它将接受一个图形上下文对象,实现实体将使用我正在使用的图形库(Java Swing /AWT 在这种情况下)。

这很有效,因为我的应用程序的 GUI 部分只需遍历模型中的所有实体并运行它们的 Draw 方法来生成可视化。最大的问题是我将我的模型与它的图形表示结合起来,我觉得这是一种不好的做法。

第二个想法

我创建了一个接受实体的EntityDrawer 类,根据实体类型选择正确的绘制方法,然后绘制到我的图形上下文。

这种方法实现了我一直在寻找的代码分离,但EntityDrawer 类严重依赖instanceOf/isA 方法来确定如何绘制实体,我多次看到这种方法被描述为糟糕的设计.我尝试重新设计它,以便类使用重载方法来确定要使用哪种绘图方法,但我意识到这基本上是相同的instanceOf/isA 方法,以更好看的方式编写。此外,对于我添加的每个新实体类,我都需要在 EntityDrawer 的代码中进行镜像,这对我来说是一种耦合形式。


我希望我的模型与其图形表示之间尽可能多地解耦,以便模型可以专注于对问题进行建模,而不是如何将自己呈现到图形上下文中。此外,将来我可能会添加更多的模型实体类型,它们将有截然不同的绘图需求。

那么,我可以使用一种模式或设计技术来实现这一点吗?我觉得我提出的两种解决方案都不是最优的,并且可能有一种设计模式可以解决这类问题。

【问题讨论】:

  • 另一种方法是实现模型的绘图方法可以使用的绘图抽象层。然后,您可以插入多个绘图实现。
  • @Henry 这个抽象层会类似于我的第二个解决方案中的EntityDrawer,还是我错过了你的意思?
  • 不,它会提供绘图图元(例如点、线和样条线)。您仍然会在调用这些绘图原语的模型类中拥有绘图方法。如果必须绘制复杂的形状,可能有几个。实际实现在抽象接口之下,可以在 swing 中渲染图元或编写 SVG 文件,或者...

标签: java oop design-patterns code-separation


【解决方案1】:

在我看来,您缺少模型、视图、控制器(或 MVC)设计模式的控制器部分。

控制器将与模型通信并与视图(您的图形部分)通信,这使您的视图和模型彼此独立。

以下是该主题的一些其他相关阅读材料: http://www.oracle.com/technetwork/articles/javase/index-142890.html

【讨论】:

  • OP 所说的一切都是 MVC 实现视图的一部分。他的“模型”只与绘制图像所代表的事物有关。此外,他没有控制器,因为在他关心的领域内没有任何东西可以控制点和向量等。
  • 在我第二次尝试解决方案时,我的EntityDrawer 是否适合担任控制器角色或控制器的一部分?
  • 我不相信它会。
【解决方案2】:

【讨论】:

  • 哎呀!那篇关于 MDA 的文章有点吓人——这样的架构系统是否适用于解决像我这样的更具体的问题,还是需要重新考虑我的程序的整个结构?如果重组意味着我将学习更好的编码习惯,我并不反对重组,我只是想知道这是否“矫枉过正”。
  • 我不知道,我不知道你的代码是如何组织的。如果您喜欢这本免费的 Microsoft 电子书,可以阅读更多相关信息:blogs.msdn.com/b/cesardelatorre/archive/2010/03/26/…
【解决方案3】:

在过去,我们用一个非常简单的接口在绘图抽象层之上构建所有这些:在点 x,y 处放置一个颜色 c 点。

然后我们为每种类型的对象编写了基于低级 API 的绘图类。

所以有一个 Point 类,它包含一个 PointDrawer,当有人调用它的 'draw()' 方法时会使用它。 PointDrawer 知道 Point 的内部结构,它可以获取颜色、x 和 y,并调用低级 API 将点放置在显示器或纸上或从花岗岩块中凿出。

然后有一个线路呼叫,它有一个颜色和两个 x,y 端点。相应的 LineDrawer 知道 Line 对象的内部结构,可以创建 Point 对象并告诉它们自己绘制,也可以调用低级 API。这个选择在于实施者。另一种方法是可以向 API 添加一个新的低级方法来绘制一条线。这样就在三个不同的层次上做出了三种选择,即 LineDrawer 如何工作以将图像显示在显示器上。

对于每个新的可绘制对象,Circle、Arc、Ellipse、Box、FilledBox 等,内部都有一个对应的 XxxDrawer,它使用三个级别的某种组合进行绘制。

请注意,有些显示器内置了绘制弧线的功能,而有些则没有。这使得选择依赖于设备,因此通常会有一组“抽屉”子类。因此,可能有一个 XyzSystems4453DeviceLineDrawer 类代替 LineDrawer。

这非常令人困惑,但它确实允许绘制每个事物以获得最大性能(如果制作动画并且您需要每秒 24 幅图像但图像中有 1000 多个弧线时很有用)而且它可以很容易地添加另一个就像现有的“除了...”的设备。 (您继承并覆盖更改的内容,有时可能会调用被覆盖的方法。)如果您保持命名和包装井井有条,您可以在代码增长时保持代码井井有条。

这一切都很重要,因为即使处理器和图形协处理器的速度提高了,我们仍然无法在我们所拥有的时间内绘制出我们需要的所有细节,而不是非常小心。

【讨论】:

    【解决方案4】:

    我认为您将模型对象与其抽屉分开的目标很重要。我将为抽屉创建一个单独的层次结构,它可以独立于模型而发展。所以我会有一个 PointDrawer 和一个 CircleDrawer 等'(可能都是从一些抽象的 EntityDrawer 扩展而来的。PointDrawer 可能是整个类层次结构的基础,这些类知道如何以各种方式(不同的颜色或不同的图形上下文)绘制一个点. 控制器应获取模型对象和适当的抽屉。您可以使用工厂...控制器将用户操作/请求转换为模型操作/查询/逻辑(例如移动),然后参与适当的抽屉

    【讨论】:

      猜你喜欢
      • 2021-05-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多