GUI 架构 已翻译 100%

ddj 投递于 2013/07/06 15:49 (共 35 段, 翻译完成于 07-30)
阅读 8166
收藏 153
6
加载中

无论是对于用户还是对于开发者而言,图形用户界面早已成为软件领域中人们习以为常的部分了。从设计角度看,GUI代表着系统设计中一组特殊的问题 —— 这些问题具有大量不同但却又有些相似的解决方案。

我的兴趣是为应用程序开发者找出对于富客户端开发有用的通用模式。我已经在许多项目评审中看到过多种不同的设计方案,而且还也见到过许多以更加易于保留下来的方式来描述的设计方案。在这些设计方案中存在着很有用的模式,但是,将模式描述出来通常都不太容易。以模型-视图-控制器为例来说,人们常常把它称为一种模式,但是我发现把它看作一种模式并不是特别有用,因为在它里面包含着许多不同的理念。在不同的地方读到MVC时会从中得到不同的理念,而且人们还会把这些不同的理念都描述为‘MVC’。如果这还不足以造成很多困扰的话,通过“传话游戏”终将造成人们对MVC误解的结局。

在本文中,我想对许多很值得关注的架构进行探讨,并为它们最值得关注的那些特性给出我的解读。我希望这样将可以为人们能够理解我所描述的模式提供一个恰当的语境。

fbm
fbm
翻译于 2013/07/08 23:36
3

在一定程度上你可以将本文看作一种用来追溯多年来各种架构下UI设计理念的思想史。然而,我在此必须提醒大家注意,架构理解起来绝非易事,特别是很多架构还在不断的变化和消亡中。要追溯出理念的传播脉络更是难上加难,这是因为同一个架构不同的人有各自不同的解读。尤其是我并未对我所描述的架构进行完全彻底的研究。我所做的只是对它们的设计进行了一般性的描述。如果这些描述有所疏漏的话,我绝对是无意的。所以,请不要将我的描述看作是这些架构的权威性说明。而且,我还舍弃或精简了我认为并不怎么切题的内容。请记住,我所关心的主要是底层的模式,而不是这些设计的发展史。

(这里有个例外,我有一个能够运行起来的Smalltalk-80环境让我可以了解其中的MVC。还得说一次,我并不认为我对MVC的调查已经做到了非常彻底的程度,但我的调查已经揭示出许多对MVC比较常见的描述中并没有给出的内容 —— 这让我进一步对我在本文中给出的对其它架构的描述保持警惕。如果你熟悉这些架构中的某一个架构并且看到有些重要的东西我给弄错了或者遗漏了,恳请告知。我认为,这个领域还可以作为一个很好的学术研究对象,加以更为详尽的调查研究。)

fbm
fbm
翻译于 2013/07/09 00:46
3

表单和控件

我将从对大家来说即简单又熟悉的架构开始进行探讨。这个架构还没有一个通用的名称,为了便于在本文中进行讨论我暂且将它们称为“表单和控件”。这个架构之所以为大家熟知,是因为早它在上世纪90年代的Visual Basic、Delphi和Powerbuilder等等类似的工具中备受推崇,直到现在仍为大家广泛使用,虽然也常为象我这样的设计狂所诟病。

为了对包括这个架构在内的所有架构进行探讨,我将使用一个共同的例子。在我所居住的新英格兰,有一个用于监测空气中冰激凌微粒数量的政府项目。如果冰激凌微粒的浓度太低,就表明我们吃的冰激凌不够多,这对我们的经济以及公共秩序而言是一种严重的风险因素。(我所喜欢使用的例子同你通常在此类技术书中看到的例子相比来说会更加真实。译者注:这恐怕是作者在开玩笑

fbm
fbm
翻译于 2013/07/09 02:33
2

为了监测我们的冰激凌的健康状态,政府在整个新英格兰州架设了相应的监测站。采用了比较复杂的空气模型,政府部门为每个监测站设置了目标值。工作人员时不时地到不同的监测站进行健康状态评估,记录下各监测站实际读到的冰激凌微粒浓度。工作人员使用下图所示的UI选择监测站,并输入日期和读到的实际值。然后系统计算并显示出实际值和目标值之间的差值。而且,如果实际值同目标值相比,低到超过了10%的程度,差值将以红色高亮显示,如果实际值同目标值相比,高到超过了5%,差值将以绿色高亮显示。

Figure 1

图1:本例所使用的UI

但我们看到这个界面时,我们会看到整个界面中有一个重要的分界线。该表单是我们的应用所专用的,但它却用到了很多通用的控件。绝大多数GUI环境带有大量的通用控件,我们可以之间将它们用在我们的程序中。我们还可以创建自己的控件,而且创建自己的控件往往还是个好主意,但是通用的可重用控件不同于专用的表单。即使专门为达到某个目的而编写的控件也可以重用于多个不同的表单。

fbm
fbm
翻译于 2013/07/09 02:57
2

该表单具有两个主要的职责:

  • 界面布局:用来定义控件在界面中的位置安排以及控件之间的层次结构。
  • 表单逻辑:完成那些在编程中很难放到控件本身之中的行为。

大部分GUI开发环境都会有一个图形化的编辑器,开发者可以在该编辑器中将控件拖拽到表单中,以此来完成对表单中大部分的界面布局的定义。采用这种方式,很容易就能够在表单中设置出一个讨人喜欢的布局(虽然这并不总是设置布局的最佳方式,我们会在下文中对此再次进行讨论。)

控件是用来显示数据的 —— 在这个例子中我们要显示的数据都是同工作人员读到的微粒值相关的数据。这些数据相当一部分总是来自其它的某个地方,在这个例子中让我们假设它们来自一个SQL数据库吧,这同大多数客户端服务器工具所假设的相一致。在大多数情况下,都会涉及到3份数据:

  • 一份数据保存于数据库之中。这份是数据的持久性记录,所以我将它称为记录装态(record state). 记录状态通常以各种不同的机制由多人来共享并以对多人可见。
  • 更进一步,还有一份拷贝保存于应用程序内存中的记录集(Record Sets) 之中。大部分客户端-服务器环境都提供了相应的工具可以帮助你很简单地得到记录集。这种数据之同应用程序和数据库的一次会话相关,所以我把这样的数据称为会话状态(session state)。 这为用户提供了数据必不可少的临时性本地存储,用户可以对这些数据进行各种处理,直到将它们保存或者提交回数据库 —— 此时这些数据就同记录状态合并到了一起。本文我将不会对记录状态和会话状态之间的协调问题进行讨论;我在[P of EAA]中对该问题所涉及的各种技术进行了探讨。
  • 最后一份数据保存在GUI组件之中。严格地讲,这些就是用户在界面中看到的数据,因此我将这些数据称为界面状态(screen state)。UI中很重要的一点是如何让界面状态同会话状态保持同步。
fbm
fbm
翻译于 2013/07/09 03:34
2

保持screen state和session state同步是很重要的。对此有个很有用的手段Data Binding(数据绑定)。基本的思路是:无论是控件表现或是控件底层的数据集,任一方发生了改变都立即把改变传播给另一方。所以如果我改变了选择的station,那么文本框控件(station id)也会跟着改变(由于底层的数据集发生了改变)。

实际上数据绑定是有点棘手的,因为你需要避免循环,比如控件状态改变了,会导致数据集的改变,数据集的改变又会更新控件导致控件状态改变.....。要避免这种情况,我们要遵循如下的使用流程:界面初始化时从session state加载数据,之后任何界面上的改变(screen state)都传播给session state. 而当底层的数据集改变后立即更新桌面控件的状态通常是不常见的,所以数据绑定可能不是完全双向的-仅限于初始的上传(指数据从session state 传到 screen state),之后数据的改变会从控件传播到session state.

d
ddj
翻译于 2013/07/08 20:28
2

数据绑定将一个客户端服务器应用程序的大部分功能处理得很漂亮。如果我改变了被更新列的实际值,甚至只是改变了选择的目标,记录集中当前所选行也会跟着变,它也会导致其它的控件的刷新。

这些行为中的大多数都是内置于框架的构造器中的,它们专注于普遍的需求,并且让这些需求很容易被满足。这是通过修改控件的特定值做到了,这些值一般被叫做控件的属性。通过一个简单的属性编辑设置其列名,从而使控件被绑定到一个记录集的特定列。

使用合适类型的参数化来进行数据绑定,可以让你走得更远。然而它并不总是会合你得意——总是有一些逻辑不能同参数化的选项相匹配。这样的场景,像计算方差(variance)就是一个不能匹配内置行为的例子——因为应用程序使它具体的依赖于形式了。

LeoXu
LeoXu
翻译于 2013/07/09 21:37
2

为了实现数据绑定,在实际字段的值发生变化时,就需要通过某种形式来通知表单,这就要求通用的文本框要调用表单上的某个特定行为。这个过程远比采用类库然后通过控制反转来调用这个特定行为的情况要复杂得多。

实现这种调用的方法有很多种 —— 客户端-服务器工具箱中最常见的就是采用事件的概念。每个控件都有一个它可以激起的事件列表。任何外部对象都可以告诉控件它对某事件感兴趣 —— 然后在发生这种事件时控件就会调用该外部对象。本质上讲,这只是观察者(Observer)模式的另一种说法而已,此时表单就是控件的观察者。通常框架会提供某种机制让开发者可以用它来编写一个子程序,然后在相应的事件发生时该子程序就会得到调用。到底在事件和子程序之间的关联关系是怎么建立的,对于我们这里的讨论来说并不重要,而且其建立的方式也会随平台的不同而不同 —— 这里的关键在于的确存在某种能够让这一切发生的机制。

fbm
fbm
翻译于 2013/07/14 00:12
2

一旦表单上的子程序获得控制权,它就可以为所欲为了。该子程序可以在执行特定的行为后,再按照需要对控件的显示内容进行修改,随后再依赖数据绑定机制将控件中的数值改变传递回会话状态。

因为并不是总是有数据绑定可用,所以这么做也很有必要。Windows控件的市场非常大,但并不是所有的控件都提供了数据绑定功能。如果没有数据绑定功能,完成同步任务的责任就非表单莫属了。这可以通过刚开始时从记录集中将数据中拿到数据并将数据设定到界面组件中,然后在保存按钮按下后将修改后的数据保存回数据库中来实现。

下面假设在数据绑定功能情况下,让我们详细看看对实际数值的编辑过程。表单对象保存了一个直接指向通用控件的应用,而且为屏幕上的每个控件都保存了一个这样的引用,但是,这里我所关心的只是实际字段、 变化字段以及目标字段。

Figure 2

图2:表单和控件的类图

fbm
fbm
翻译于 2013/07/19 17:05
2

文本域为文本的改变声明了一个事件,当表单在初始化期间组装屏幕时,它自身领取了那个事件,将一个方法绑定到其自身的——这里是一个actual_textChanged实际值文本变更)。

Figure 3

图3:使用表单和控件改变样式(genre)的序列图

当用户改变实际值得时候,文本域控件就会提升其事件,并且通过框架的魔术使得actual_textChanged的绑定得以运行。方法从实际的目标文本域中得到了文本值,做一些截取(subtraction),而后把这个值放到变化域(variance field)中。它也会计算出这个值应该用什么颜色来显示,而去适应恰当的文本颜色。

我们能用一些简评来总结该架构:

开发者编写使用了通用控件的应用程序定制表单。

表单描述了其控件的布局。

  • 表单监听着控件,并且有处理方法针对控件提交上来的感兴趣的事件作出响应。
  • 简单的数据编辑通过数据绑定受到处理。
  • 复杂的变更则在表单的事件处理方法中完成。

模型视图控制器(Model View Controller)

在UI开发领域被最广泛引述的模式很可能就是MVC(Model View Controller)了——它也是最为人所不确切引用的。我已经计算不出来我有多少次看到些一些东西被描述成为MVC,而最终被证明一点都不像MVC了。坦率点讲,发生这种情况许多是部分由于传统的MVC不怎么真正理解现在的富客户端。而目前先让我们来瞧一瞧它的起源。

我们看待MVC时,有一点很重要,那就是这是在任何规模上进行严肃的UI工作的首次尝试之一。图形用户界面在上个世纪70年代还并不怎么普及。我前面刚刚描述过的表单和控件模型出现在MVC之后——我首先描述它们是因为它们更加简单——但是请注意,我有意通过进行一些针对Smalltalk80实际细节上玩味来进行这一描述 —— 一开始它只是一个单色(monochrome)系统。

LeoXu
LeoXu
翻译于 2013/07/20 22:38
2
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(10)

cosyman
cosyman

引用来自“DISSECTOR”的评论

不解释还明白,一解释反而更糊涂了。看了一下原作者,原来是名词制造者Martin Flower。
数据共享,逻辑分层,消息驱动,展现与控制分离,展现与交互结合,一个普通程序员都能理解的技巧,有必要弄成一本要啃一个月才明白的新书吗?

面向对象,就四个字,可是有很多书来帮助理解
DISSECTOR
DISSECTOR
不解释还明白,一解释反而更糊涂了。看了一下原作者,原来是名词制造者Martin Flower。
数据共享,逻辑分层,消息驱动,展现与控制分离,展现与交互结合,一个普通程序员都能理解的技巧,有必要弄成一本要啃一个月才明白的新书吗?
shikeaiDev
shikeaiDev
这篇文章其实不错的。
suikhan
suikhan
Icove
Icove
+1
ardorleo
ardorleo
喜欢这篇文章
mallon
mallon
观念太老土了
edgarliuxq
edgarliuxq
顶一下
_Elvis
_Elvis
+1
贾珣
贾珣
+1
返回顶部
顶部