接口层次设计模式?

时间:2009-11-17 08:44:46

标签: c++ design-patterns user-interface mobile

我正处于开发具有图形用户界面的C ++多平台(移动)应用程序的早期阶段。我正在尝试找到一种抽象实际UI /窗口实现的好方法。这基本上是我到目前为止所尝试的:

我创建了一个界面层次结构

Screen
 +Canvas
 +Form
   +Dialog
Control
 +EditBox
 +CheckBox
 +...

我还有一个类Application,它基本上使用所有这些接口来实现UI逻辑。 Application类还提供了用于创建UI类实例的抽象工厂方法。

这些接口都有实现。例如,对于Windows Mobile,Win32Form实现Form,Win32Canvas实现Canvas和Win32Dialog实现Dialog。正如您所看到的,问题是实现失去了层次结构,即Win32Dialog不会扩展Win32Form。

这在例如Form :: addControl(Control& ctrl)方法中成为一个问题。在Windows Mobile版本的应用程序中,我知道ctrl参数是Win32 ...控件实现之一。但是由于层次结构丢失了,所以无法知道它是Win32EditBox还是Win32CheckBox,我真的需要知道它才能在控件上执行任何特定于平台的操作。

我正在寻找的是如何解决这个问题的一些设计模式。请注意,不需要保留此接口层次结构解决方案,这只是我当前的方法。我会采取任何方法来解决让UI逻辑与几个不同的UI实现分开的问题。

请不要告诉我使用这个或那个UI库;我喜欢从头开始编写代码,我在这里学习经验......; - )

6 个答案:

答案 0 :(得分:2)

我身边的一些灵感:

不要从屏幕继承Form。这不是“是一个”的关系。

 Screen
 Canvas
 GUIObject
    Form
       Dialog
    Control
       Button
       EditBox
       CheckBox
       ...
       Panel

更详细的说明:

屏幕

  • “has-a”canvas
  • 提供显示特定功能
  • 不要试图从Canvas继承

帆布

  • 您抽象的绘图环境
  • 目标是屏幕画布,表格画布,图像,记忆......

GUIObject

  • Wnd in some hierarchyies
  • 通常提供一些内省功能
  • 通常提供消息处理接口

表格

  • 在一些工具包中实际上称为“对话框”
  • “有一个画布,你可以画画”
  • “有一个”控件列表或客户区面板
  • “有一个”标题,滚动条等。

对话框

  • 我喜欢称对话标准化为YesNo,OpenFile等,基本上简化了Form(没有关闭按钮,没有菜单等)。
  • 可能会也可能不会从表格
  • 继承

控件有望不言自明(Panel是带有控件的区域,带有可选的滚动条)。

这应该有效。然而,如何在不破坏该界面的通用性和/或易用性的情况下利用每个平台的特定特征是困难的部分。我通常会尝试将所有绘图与这些基本类分开,并拥有一些特定于平台的“Painter”,它可以采用每个基本对象并以特定于平台的方式绘制它。另一个解决方案是每个平台的并行层次结构(我倾向于避免这个,但有时需要 - 例如,如果你需要包装窗口句柄)或“功能位”方法。

答案 1 :(得分:1)

您可以使用遏制。例如,实现Dialog的类将包含Win32Dialog作为属性,而实现Form的类将包含Win32Form;对Dialog或Form成员的所有访问都将委托访问包含的类。这样,如果这对您的项目有意义,您仍然可以Dialog继承Form

话虽如此,我在你的界面设计中看到了一些奇怪的东西。如果我理解正确(我可能没有,那么请纠正我),Dialog继承自Form,而Form继承自Screen。问自己这个验证这个设计:对话也是表格吗?它是否也是一个屏幕?仅当是-a 关系有意义时才应使用继承。

答案 2 :(得分:1)

解决此问题的GOF模式是Bridge Pattern。 Konamiman的回答解释了它,你使用委托/遏制来抽象其中一个问题。

答案 3 :(得分:0)

我花了数年时间做手机开发。

我的建议是你不要陷入'制造抽象'陷阱!

这样的框架比在每个平台上实现应用程序更多的代码(和令人厌烦的地狱,而不是有趣!)。

抱歉;你正在寻找乐趣和经验,我会说有很多有趣的地方可以获得它。

答案 4 :(得分:0)

首先,主要问题似乎是“抽象”层中没有抽象。

实现与操作系统提供的控制相同的控件,并简单地用“Win32”为其名称加前缀不构成抽象。

如果你不打算简化和概括结构,为什么还要先写一个抽象层?

为了实际上有用,你应该编写代表所需概念的类。然后在内部将它们映射到必要的Win32代码。不要仅仅因为Win32有一个Form类或Window类。

其次,如果你需要精确的类型,我会说放弃OOP方法,并使用模板。

而不是功能

Form::addControl(Control &ctrl)

定义

template <typename Ctrl_Type>
Form::addControl(Ctrl_Type &ctrl)

现在该函数知道控件的确切类型,并且可以执行您想要的任何类型特定的操作。 (当然,如果必须将控件保存到控件中的列表中,则会再次丢失类型信息。但类型信息会进一步传播,至少可能会有所帮助。

该集合可以存储类似boost::variant对象的内容,甚至可以保留某些类型信息。

否则,如果你要去OOP路线,至少正确地做。如果您要隐藏常见IControl接口后面的EditBoxes和CheckBoxes,那么应该是因为您不需要确切的类型。界面应该是您所需要的。这就是界面中的重点。

如果不遵守该合同,如果您要一直向下转换为实际类型,那么您的OOP层次结构是有缺陷的。

如果你不知道自己想要达到什么目标,那将很难取得成功。这个试图编写“由Windows公开的用户界面的抽象”的目的是什么,你什么也没做,因为那是Windows 已经暴露的东西。如果Windows公开的抽象不符合您的需求,请编写抽象层。如果你想要一个不同的层次结构,或根本没有层次结构,或者没有一切都是Form或Control的层次结构,那么编写一个抽象层是有意义的。如果你只想要一个“与Win32相同,但我的命名约定”的图层,那么你就是在浪费时间。

所以我的建议是:从你希望GUI层的工作方式开始。然后 担心它应该如何映射到底层的Win32原语。忘记你曾经听说过表格或对话框或编辑框,甚至是控件或窗口。所有这些都是Win32概念。定义 应用程序中有意义的概念。没有规则GUI中的每个实体都必须从公共Control类或接口派生。对于要放置的所有东西,没有规则必须有一个“窗口”或“画布”。这是Windows使用的约定。编写一个对有意义的API。

然后根据可用的Win32原语找出如何实现它。

答案 5 :(得分:0)

有趣的问题,所以有几点想法:

  • 首先:你总是可以使用MI,以便Win32Dialog继承Win32Form和Dialog,因为你得到一个漂亮的钻石你需要虚拟继承
  • 第二:尽量避免MI;)

如果你想使用特定的控件,那么你使用的是Win32特定的方法,对吗?我希望你不是在谈论up_casting:/

那么,当Win32特定的控件方法有一个更精确的类型时,它是如何被赋予通用对象的?

你可以在这里应用得墨忒耳定律。使用Facade方法效果很好。

这个想法是你实例化一个Win32Canvas,然后要求画布添加一个对话框(带有一些参数),但你实际上从未真正操纵过对话框,这样画布就知道它的实际类型,并且可以执行特定于平台。

如果你想稍微控制一下,请让画布返回一个Id,你将来会用它来定位这个特定的对话框。

如果canvas从模板继承(在平台上模板化),则可以缓解大多数常见实现。

另一方面:Facade实际上意味着一个膨胀的界面...