如何最好避免编写writing肿的GUI代码?


48

我发现每当使用GUI代码时,该代码往往会比其他类型的代码膨胀更快。似乎也很难重构。在其他类型的代码中,我可以轻松地重构-我发现可以将较大的类分解为较小的功能-对于大多数GUI框架,我经常绑定到需要我的小部件/控件/任何类的框架在窗口小部件/控件/任何地方更直接地实现更多功能。有时这是由于需要(a)继承一些基本控件/控件/事物,或者(b)需要访问受保护的方法。

我通常还必须(例如)通过信号/事件/来自框架的任何内容来响应各种各样的输入,以实现与用户交互的所有模式。我可能需要一个GUI小部件/控件来处理各种各样的输入/输出,其中可能包括:

  1. 右键单击/上下文菜单
  2. 对上下文菜单中的选择做出反应-可能很多
  3. 绘制GUI的一种特殊方法
  4. 对键盘输入做出反应
  5. 按钮,复选框,

...一直在GUI之下管理代表业务逻辑的类。

一个简单而直接的GUI可以使其代码快速增长,即使分离出业务逻辑并使用MVC时,我也发现GUI代码具有很大的吸引力。

有什么方法可以以合理的方式管理GUI代码,并避免使其成为损坏的窗口?还是大量随机事件处理程序/重写方法确实是我们可以为GUI代码做的最好的事情?


4
您对“膨胀”的确切定义是什么?

Answers:


36

关于GUI代码,要记住的是它是事件驱动的,而事件驱动的代码总是会出现大量随机组织的事件处理程序。当您尝试将非事件驱动的代码插入类时,它会变得非常混乱。当然,它具有为事件处理程序提供支持的外观,并且您可以保持事件处理程序的大小,但是所有这些额外的支持代码浮动使您的GUI源代码显得肿且混乱。

那么,您该如何处理?如何使事情更容易重构?好吧,我首先将重构的定义从偶尔执行的工作更改为在编写代码时不断进行的工作。为什么?因为您希望重构使您能够更轻松地修改代码,而不是相反。我不仅在这里要求您更改语义,而且还要求您进行一些心理健美操以便以不同的方式查看您的代码。

我发现我最常用的三种重构技术是RenameExtract MethodExtract Class。如果我从未学习过其他任何重构,那么这三者仍然可以使我保持代码的清洁和结构化,并且从您的问题的内容来看,我觉得您很可能会发现自己几乎在不断地使用相同的三种重构。为了使您的GUI代码保持简洁。

您可以将世界上最好的GUI和业务逻辑分离,而GUI代码仍然看起来像是在其中部引爆了一个代码矿。我的建议是,有一个或两个以上的类来帮助您正确管理GUI并没有什么害处,而且如果您应用MVC模式,则不必一定是View类-尽管经常会发现中介类与您的观点如此相似,以至于您经常会感到为方便起见将它们合并的冲动。我对此的看法是,添加额外的特定于GUI的层来管理所有可视逻辑并没有什么害处,但是您可能想要权衡这样做的收益和成本。

因此,我的建议是:

  • 除了调用和定义GUI如何挂接到View(或中间层)之外,在GUI后面不执行任何操作。
  • 不要试图将与视图相关的所有内容统统归类到一个类中,甚至不要在每个GUI窗口中将它归类为一个类,除非这样做对您有意义。您的替代方法是创建许多小的且易于管理的类来管理您的GUI逻辑。
  • 当您的方法开始看起来比4-5行代码略大时,请检查这是否有必要,以及是否有可能提取一个或两个方法,以便使您的方法保持精简,即使这意味着一个类还有更多方法
  • 如果您的类开始看起来真的很大,请先删除所有重复的功能,然后查看是否可以在逻辑上对方法进行分组,以便提取另一个或两个类。
  • 考虑每次编写一行代码时进行重构。如果您可以使用一行代码,请查看是否可以对其进行重构以避免重复功能,或者使其更精简而不更改其行为。
  • 接受不可避免的事实,即您将始终感到系统中的一个或另一个部分开始感到有些start肿,尤其是如果您在进行过程中忽略重构时。即使有了良好的代码库,您仍然会觉得自己还有更多可以做的事情。这就是编写软件的现实,您会发现自己总是觉得可以做得更好一些,因此您需要在做专业工作和镀金之间取得平衡。
  • 接受您尝试保持代码清洁的代码越显得code肿的想法。

3
+1不管您喜欢与不喜欢,GUI都会执行大量的详细操作,这意味着需要编写代码。
Patrick Hughes 2012年

开发人员应学习将事件驱动的编码用于GUI。
David Gao

23

我认为您遇到的许多问题都可以追溯到一个简单的原因。大多数开发人员不会将GUI代码视为“真实”代码。我这里没有证据或统计数据,只是我的直觉。

也许他们认为这只是“介绍 ”,并不重要。“ 没有任何的业务逻辑有 ”,他们说,“ 为什么单元测试 ”?当您提到面向对象和编写简洁的代码时,他们会大笑。他们甚至不尝试使事情变得更好。首先没有结构,他们只是拍一些代码,然后随着其他人逐渐添加自己的风格而使其腐烂。一个美丽的烂摊子,涂鸦代码。

GUI代码具有其独特的挑战,因此必须区别对待并予以尊重。它需要爱和想要编写它的开发人员。那些将使它变薄并提供良好的结构和正确的图案。


2
+1暗示对GUI代码的理解与对非GUI代码的区别。我忘了听到有人说“不要费心测试GUI,因为它不具有成本效益,而且很难做到”的次数也无法计算。我通常会翻译为“这很困难,而且我懒得学习如何做!”。
S.Robins 2012年

1
+1在我工作的地方,我们通常不审查GUI代码-“这只是GUI,请跳过它”。我和任何人一样内。奇怪的是,在我的个人项目中,如果有时间尝试获得漂亮的干净GUI代码,我会花费很多。猜猜这只是一种文化。
HappyCat 2012年

8

由于某些原因,GUI代码在开发人员中造成了关注点分离的盲点。也许是因为所有教程都将所有内容都归为一类。也许是因为物理表示使事物看起来比实际紧密得多。也许是因为课程建立缓慢,所以人们没有意识到他们需要重构,例如众所周知的青蛙因慢慢升温而沸腾。

无论出于何种原因,解决方案都是使您的类变小得多。为此,我不断问自己是否可以将自己输入的内容放在单独的课程中。如果有可能放在另一个类中,并且我可以为该类想到一个合理且简单的名称,那么我会这样做。


6

您可能需要看一下Model View Presenter / Passive View模式。Ray Ryan在Google IO上就GWT的最佳架构做法进行了精彩演讲。

http://www.google.com/events/io/2009/sessions/GoogleWebToolkitBestPractices.html

将思想抽象为其他框架和语言很容易。MVP的主要好处(在我看来)是单元可测试性。而且,如果您的代码没有肿且没有意大利面条,那么您只会得到(根据您的问题判断,这就是您想要的)。它通过引入称为presenter的视图逻辑层来工作。实际的视图通过接口与之分离(因此可以在单元测试中轻松模拟)。现在,由于您的视图逻辑层(演示者)已摆脱了具体的GUI框架的内部结构,因此您可以像常规代码一样组织它,而不必依赖于Swings继承层次结构。理想情况下,只要它们遵循相同的界面,就可以在不同的框架中切换GUI实现。


1
+1。MVP完全专注于如何将GUI逻辑提取到单独的类中,这通常与人们谈论MVC时所了解的完全不同。
Doc Brown

5

我的答案包括四个部分:结构,简单性,测试和语法。

前三个真的很难做到!

结构意味着要特别注意使用最少的代码和最多的框架,库等。

简单意味着从最初的设计到实际的实现都保持简单。保持导航简单,使用简单的插件,使布局相当“平整”将对这里有所帮助。现在,可以将它们“出售”给客户/用户,从而可以快速查看可在PC,iPad,移动设备和其他设备上运行的页面的优势。

测试意味着包括浏览器测试工具(webrat和capybara在我的工作中会想到),当可以设计更好的代码来开始处理跨浏览器问题时,它们可以预先捕获跨浏览器的问题,而不是频繁地“修补”代码由不同的开发人员使用,因为它们是由不同浏览器的用户“发现”的。

句法。 在HTML,CSS,Javascript等中使用代码检查器/ IDE /编辑器插件等确实很有帮助。当不同的浏览器执行不同的操作时,浏览器通过处理格式错误的HTML而获得的优势将对您不利。它,因此检查您的HTML格式的工具至关重要。拥有格式正确的HTML对于无膨胀的HTML很有帮助,因为错误的代码应具有更高的可见性。


4

我发现的解决方案是声明性代码。仅使用过程代码是意大利面条GUI代码的秘诀。当然,“一种特殊的方式来绘制窗口小部件”可能会保留代码。但这是在类中隔离的代码。事件处理程序,键盘快捷键,窗口大小-最好声明所有杂乱的内容。


4

这里有很多很棒的答案。

帮助我简化GUI代码的一件事是确保GUI具有自己的数据模型。

举一个简单的例子,如果我有一个带有4个文本输入字段的GUI,那么我有一个单独的数据类,该数据类保留了这4个文本输入字段的内容。更复杂的GUI需要更多数据类。

我将GUI设计为模型-视图。GUI模型由应用程序模型的应用程序控制器-视图-控制器控制。应用程序视图是GUI模型,而不是GUI代码本身。


2

诸如文字处理,图形编辑器之类的应用程序具有复杂的界面,其代码也并非简单。但是,对于业务应用程序,GUI不必太复杂,而仍然有些复杂。

简化GUI的一些关键是(大多数适用于.NET):

  1. 力求尽可能简化设计。如果企业没有要求,请避免幻想行为。

  2. 使用良好的控件提供程序。

  3. 不要在客户端代码本身中创建自定义控件功能。而是,以扩展原始控件的方式创建用户控件,以便您可以在控件中而不是在使用表单/页面的代码中反映您的特定行为。

  4. 使用一个框架(甚至是一个家庭开发的框架)来处理国际化,资源管理,样式等,以便您不必在每个UI中都重复此代码。

  5. 使用组件(或框架)进行导航。

  6. 建立有关错误,警告,确认等的标准对话。


1

将面向对象的设计应用于您的代码,并用于UI开发:

  1. 分开的表示和模型 使用MV-无论哪种库/框架,或编写自己的MV,以帮助将视图/控制器逻辑与数据模型分开。与后端的所有通信应在模型内部完成,并且模型状态应始终与后端同步。
  2. 解耦 如果对象A知道对象B,则A可以调用B上的方法,但是B不应该知道A。相反,A可以侦听来自B的事件。它确保没有循环依赖。如果您的应用程序在组件之间有很多事件,请创建一个EventBus,或利用事件驱动的框架(例如Twitter Flight)。
  3. 部分渲染还是完全渲染如果您的视图是表或项目列表,则可能会尝试创建诸如“添加”,“删除”之类的方法以将一个项目插入/从集合中删除。当您必须支持排序和分页时,您的代码很容易膨胀。因此,我的建议是:即使部分更改,也只需重新渲染整个视图。性能如何?好吧,如果您的收藏很大,那么您还是应该进行分页。Web开发人员:确保将事件处理程序委派给视图的不变的根元素。
  4. 视图模型例如,当视图的状态变得太复杂而难以维护时,表视图必须跟踪行数据,列数据,排序顺序,当前选中的行(如果它支持多重检查)等,为这些状态创建一个ViewModel对象。如果UI发生更改(例如:用户检查一行),则View对象应在ViewModel上调用setter。并且应该通过更新UI来响应ViewModel的change事件。通常,如果更改事件是由UI触发的,则应避免更新UI。

这是一个小巧但不平凡的应用程序,可帮助说明我的一些观点。您可以在此处找到代码和查看/模型交互图:https : //github.com/vanfrankie/pushpopbox


0

您想看一看“数据绑定”的概念。这是一种以声明方式将UI元素连接到抽象模型元素的方法,以使模型元素自动与UI内容同步。这种方法有很多好处,例如,不必自己编写事件处理程序即可同步数据。

许多UI框架都有数据绑定支持,例如.NETEclipse / JFace

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.