Saleri

The Essence of Software.Chapter.08.概念映射

最后一次修改于

你可以将应用程序的概念视为在后台运行,位于用户界面之后。界面提供了激活概念操作的按钮,以及概念状态的可视化。因此,当用户点击社交媒体帖子上的“喜欢(like)”按钮时,就会激活形如 upvote.like(u,p) 的操作,通过其 like 操作告诉 upvote 概念,用户 u 认可帖子 p。此操作的结果——upvote 概念状态的变化——随后反映在显示给用户的更新后的点赞数中。

创建用户界面不仅仅涉及视觉设计;其本质是设计一种从底层概念到界面中具体形式的映射(mapping)。界面设计师通常通过创建多个屏幕和对话框(由流程和链接连接),然后在其中嵌入连接概念操作和状态的控件及视图,来塑造这种映射。

人机交互研究人员对映射的设计进行了广泛的研究,而且已制定的准则——主要在设计的物理和语言层面——同样适用于用概念设计的系统。

但是概念提供了一个机会来改进(甚至可能重新思考)概念层面设计与物理和语言层面之间的关系。因此,本章重点介绍一些示例,这些示例说明了映射设计可能是多么棘手和复杂,以及它必须在多大程度上受到底层概念的启发。

如何让一个简单的概念变得难以使用

即使底层概念很简单,仍然有可能设计出难以使用的映射。上周,我的桌面上弹出一个消息,询问我是否要升级到最新版本的 Oracle Java。我点击了“是”。当我运行安装程序时,它显示了一个带有两个按钮的对话框,一个标记为“安装(Install)”,另一个标记为“移除(Remove)”(图 8.1)。

图 8.1 Java 安装过程中令人困惑的对话框。“移除”是什么意思?

现在你可能会想,这里没有令人困惑的地方,但我还是设法让自己困惑了。大概“安装”按钮会调用安装新版本软件的操作。但是“移除”按钮是做什么的?显然,它删除了我当前安装的任何版本,然后什么也不做。

这些似乎是合理的解释;毕竟,“安装”和“移除”这两个词大致就是这个意思。那么为什么我会困惑呢?首先,我刚刚是为了响应升级提示而下载并运行了安装程序,所以我的目标不太可能仅仅是删除旧版本。此外,许多安装程序让你选择要么删除旧版本,要么用新版本替换它,或者仅仅安装新版本以便你选择运行哪个版本。由于“移除”按钮被突出显示为默认选项,这似乎是最有可能的解释。

也许这个对话框的设计师意识到了这种困惑,这就是为什么它包含了一个唐·诺曼(Don Norman)可能会称之为“用户手册”的东西:包含一句话的冗长解释,告诉你“安装”和“移除”是做什么的。

本可以怎么做呢?首先,我们可能会注意到 install 概念有两个截然不同的操作原理,一个是你安装并使用一个应用程序,另一个是你卸载它并回收空间。如果意图是支持这两种情况,它们本可以作为单独的工作流提供,也许在单独的选项卡中(并且以安装程序作为默认选项,特别是在遵循升级提示时)。其次,移除旧版本以用新版本替换与卸载截然不同。前者可以作为“安装”按钮旁边的一个选项(“移除旧版本?”)出现;后者可以标记为“卸载”而不是“移除”。

图 8.2 change.org 请愿书发起者的视图揭示了一个小骗局:向签名者显示的计数(请愿书正文右侧的 683)低于发起者可见的实际计数(左上角管理栏中的 698)。

无论如何,可怜的读者,我怀疑你已经听够了这个平平无奇的例子。只想说明概念问题潜伏着,即使在只有一个带有常见英文单词的两个按钮的对话框后面也是如此。

在界面中包含用户手册

有时候,一个概念足够复杂,以至于即使是最好的设计师也无法在没有额外解释的情况下传达操作或状态的含义。在第 2 章中,我描述了 Backblaze 的消息“截至今天下午 1:05 的备份已完成(You are backed up as of: Today, 1:05 PM)”由于其 backup 概念的复杂性而具有误导性。

这并不意味着在下午 1:05 之前保存的任何文件都已安全备份。为了反驳这种解释,我可能会将消息改写为“最后一次备份:今天下午 1:05”,并在下面添加一句话,比如“此备份包含在下午 12:48 开始扫描之前保存的所有文件”。或者,更保守地说,我可能改为显示消息“您的备份截至:今天下午 12:48”,并在下面附加“此备份于下午 1:05 完成”。

图 8.3 邀请支持 change.org 的请愿书。但这真是吗?

这正是苹果公司在其 do not disturb 概念的对话框中所采用的方法。在“允许重复来电”的复选框(其解释远非显而易见)下方,较小的灰色字体注释补充道:“启用后,同一人三分钟内的第二次来电将不会被静音。”

暗黑模式:有意的混淆

公司可以引导用户做出违背其利益的行为,执行一个操作而不是另一个操作,或者根本不执行操作。通常这是通过有意(且恶意)混淆底层概念的映射来完成的。

请愿网站 change.org 就包含几个这样的混淆。几年前,我发起了一项请愿,试图说服我们的市长不要在当地公园中间设立新的市政建筑。我注意到,每次我查看自己的请愿书(图 8.2)时,签名者数量似乎都在一秒一秒地增长,数字随着我的注视而上升。

后来我意识到,因为我是请愿书的发起者,屏幕左上角仅对我显示的计数揭示了实际的支持者数量。如果我稍等片刻,几秒钟后增长的计数总是会停在那个实际数字上。每次显示请愿书时,计数总是从低于实际数字开始,然后立即开始上升,给人一种实时活动的假象。

图 8.4 两个按钮,一个叠在另一个上面(在网站上分别为黄色和蓝色),表面上对应 Amazon Prime 概念的不同操作,但实际上都绑定到同一个操作。

更阴险的是,当你签署请愿书后,你会收到捐款请求(图 8.3)。大多数人会非常合理地假设这个 donation 概念与 petition 概念是以特定的方式同步的:他们的捐款会被传递给请愿组织者,以帮助他们资助事业。事实上,这笔钱是用来支付在 change.org 上的广告费用(其域名后缀也有误导性:它不是非营利组织)。就我自己的请愿而言,支持者向 change.org 捐献了 2000 多美元。如果我理解了“众筹(chipping in)”的操作原理,我一定会警告我的签名者不要这样做。

有时,映射会混淆按下按钮时发生的操作。在亚马逊英国网站上,我获得了一个注册免费试用 Amazon Prime 的机会,并看到了似乎是两个按钮:一个标有“免费试用 Prime(Try Prime FREE)”的黄色按钮和一个其下方标有以“继续(Continue)”一词开头的蓝色按钮(图 8.4),暗示这可能意味着“继续而不使用 Prime”。实际上,完整的按钮标签写着:“继续并使用免费次日达(Continue with FREE One-Day Delivery)”。也就是说,两个按钮都会激活 signup 操作!想要注册而继续的选项是通过点击按钮左侧非常不显眼的蓝色链接提供的。

映射可以通过简单地隐瞒重要信息或使其难以访问来控制你的行为。例如,许多航空公司让你很难找到你的常旅客里程过期日期,希望它们在你意识到之前就过期了。(这只是 frequent flyer 概念往往被设计成违背飞行者利益的一个方面。)同样,PayPal 也被指责隐藏用户的账户余额,这——加上缺乏将收到的资金自动转移到外部银行账户的同步机制——使用户在不知不觉中(以及 PayPal 获利的情况下)保持最大的余额。

图 8.5 Gmail 中的标签:一个带有“hacking”和“meetups”两个标签的对话。

映射复杂的组合:Gmail 标签的奥秘

谷歌的电子邮件服务 Gmail 提供了 label 概念来组织消息。例如,你可以定义一个标签 hacking,并将它附加到与极客朋友讨论编程的消息上。然后,如果你想找到你记得的有关编程的早期消息,你可以过滤该标签。

这是我之前提到的协同组合的一个很好的例子(在第 6 章中)。通过在已发送和已删除消息中使用特殊的“系统标签”,label 概念统一了所有类型的查找。例如,带你查看已发送消息视图的标有“已发送(Sent)”的按钮,只需调用对 sent 标签的标签查询即可。

Gmail 还提供了 conversation 概念。它的目的是将与特定讨论线索相关的消息分组在一起,以便你可以一起看到消息、回复、回复的回复等等。

将这些概念组合并映射在一起具有挑战性。Gmail 的设计师选择仅将标签附加在消息上,但要在对话上显示标签。这导致了一些奇怪的反常现象。在图 8.5 中,一个对话似乎带有两个标签,hackingmeetups。果然,如果你分别过滤任何一个标签,该对话都会出现。但是,如果你同时过滤两个标签,该对话将不会显示(图 8.6)。

虽然令人惊讶,但这并不是一个错误。显示在对话上的标签是它包含的所有消息累积标签的总和。在这个例子中,对话中的一条消息带有标签 hacking,另一条带有标签 meetups。因此,对话被显示为带有两个标签。但由于没有单条消息同时带有这两个标签,将两者结合过滤不会产生任何结果。

图 8.6 Gmail 中的标签过滤惊奇:一个对话似乎同时匹配“hacking”和“meetups”标签,但如果你同时查询它们却不会显示。

你可能想知道一个对话中不同的消息如何最终带有不同的标签。当你选择一个对话并添加标签时,该标签会被添加到对话中的每一条消息上。但你可以定义规则,根据内容为传入消息附加标签。而且,当你向对话添加标签时,它只会影响当前属于对话的消息;以后添加的消息不会自动继承该标签。此外,一些标签(如 sent 标签)会自动应用于单条消息。

在实践中,这种设计最令人烦恼的后果更为平常:当你过滤一个标签时,你会得到包含该标签消息的所有对话,但你无法知道对话中具体是哪些消息实际带有该标签。

例如,当你点击 Gmail 中的 sent 来查看已发送的消息时,你得到的是一个对话列表,其中嵌入了已发送消息,其中也包含并非由你发送的消息。Gmail 的设计师通过默认显示已发送消息展开,并折叠其余消息来减轻这个问题(图 8.7)。但这种区别并不总是容易看出。88^

图 8.7 在 Gmail 中过滤已发送消息:已发送的消息是两条中的第一条。

更糟糕的是,这种默认展开策略似乎只应用于已发送消息。在其他情况下,对话中的所有消息都被折叠,除了最近的一条消息。这种差异证明 Gmail 的设计师意识到了这个问题,但尚未解决它。

易懂但难以使用:Backblaze 恢复

在迄今为止我们看到的所有示例中,问题都在于用户界面不够清晰:对界面控件和视图的含义(就底层概念而言)存在不确定性。相反,有时候含义足够清晰,但映射使得用户难以执行操作或获取所需信息。

Backblaze(正如我在第 2 章中提到的)是一个我已经使用了几年的出色备份实用程序。备份速度确实非常快(每天最高可达约 200 GB),设置很简单,而且服务似乎很可靠。此外,恢复文件的最新版本很容易:只需访问其网站的恢复页面,选择文件并点击下载即可。

然而,恢复旧版本并不是那么容易。对话框(图 8.8)让你在文件系统中导航(左侧)以找到你感兴趣的文件夹,并(右侧)让你选择要下载该文件夹中的哪些文件。

要恢复文件的较早版本,你可以在对话框顶部输入日期。实际上有两个日期。通过设置 from(从)日期,你可以仅包含该日期之后修改的文件;通过设置 to(至)日期,你可以选择将恢复哪个版本。例如,如果你选择从 2021 年 1 月 1 日 to 2021 年 3 月 1 日的文件,则仅向你显示在年初之后修改(或创建)的文件,你将恢复的版本是截至 3 月初备份的最后一个版本。

图 8.8 用于恢复文件的 Backblaze 对话框:from 和 to 日期会过滤显示哪些文件版本,这看似合理的映射。

这听起来不错,但有一个障碍。假设你发现一个重要文件不知何故损坏了。你想恢复上一个未损坏的版本。如果你知道损坏发生在哪一天,你可以直接在对话框的 to 框中输入该日期。但是,如果你不知道日期,你现在就必须通过旧版本进行搜索来找到它。

你可能会从昨天开始,每次往回退一天,恢复文件并进行检查,直到找到未损坏的版本。如果你知道损坏发生在 1 月 1 日至 3 月 1 日之间的某个时间,你会从 3 月 1 日开始,不断地恢复并检查,直到(在最坏的情况下)你检查了 60 次并一直回退到 1 月 1 日。

这可能看起来不是什么大不了的事,但不幸的是,每次你在对话框中更改 to 日期时,你都必须重新加载文件夹树(这需要大约 20 秒),然后,由于树已重置,你必须再次向下导航到感兴趣的文件。然后你必须下载它并进行检查。

毋庸置疑,这是一个费力的过程。底层概念很好:旧文件版本都可以访问;问题是映射让你很难得到它们。解决这个问题的一个可能方案(被 Carbonite 和 Crashplan 等其他备份实用程序所采用)是将文件的所有版本连同其修改日期一起显示,并让你一次性下载它们(图 8.9)。

图 8.9 用于恢复文件的 Carbonite 对话框,它显示了一个文件的所有版本列表。

实时过滤难题

假设一个应用程序允许你显示一个项目集合,这些项目由它们都满足的某个属性来定义;而且,在查看该集合时,你可以修改这些项目。设计问题如下:如果你对项目的修改使该项目成为集合成员的属性失效,会发生什么情况?

Apple Mail 的 flag 概念符合此模式:有七种不同颜色的标志(flag),用户可以按自己的喜好解释它们,并有操作将标志附加到消息上,以及显示带有给定标志的所有消息。

flag 概念将其状态维护为从消息到它们所带标志的映射。当你单击左侧侧边栏中的标志图标时,应用程序将显示带有该颜色标志的所有消息(图 8.10)。

一开始你可能会想,点击一个标志映射到 flag 概念中的一个操作以查找所有带有该标志的项目。但最好将点击视为更精细映射的一部分,该映射将界面切换到显示带标志项目的模式。这样更好,因为它允许界面是实时的,如果带标志消息的集合自发改变(例如,由于刚才到达了一条消息并被规则标记),它就可以动态更新。

现在的难题是:如果在查看带有标志的消息列表时,你选择了其中一条,然后移除了它的标志,会发生什么情况?一个看似显而易见的解决方案是将该消息立即从列表中移除,以确保一致性(即显示的消息恰好是带有给定标志的那些)。

图 8.10 Apple Mail 中 flag 概念的巧妙映射:显示带有黄色标志的消息。第一条消息的标志已被移除,但该消息仍在列表中。

然而,在实践中,这将是一个糟糕的映射设计。想象一下如果你不小心切换了一条消息的状态,移除其标志后又想恢复它,会发生什么。当你移除标志时,该消息将从列表中消失,你的选择也随之清除。你甚至可能找不到那条消息来恢复它的标志——这具有讽刺意味,因为你可能会仅仅因为某些消息难以被发现而恰恰标记它们![92]

也许与直觉相反,更合适的行为是不更新显示,并保留最初显示的所有那些消息。当你取消一条消息的标志时,该消息仍保留在其中;但如果你切出标志视图,稍后再返回,你就会发现该消息不再出现。

为了使该方案起作用,必须在列表中单独对每条消息标记标志。起初这似乎没有必要,因为从定义上讲,初始显示中的每条消息都必须带标志。但是,当你切换消息上的标志时,你可以看到标志消失,而该消息仍然留在那儿——并且仍被选中——让你很容易恢复该标志。这正是 Apple Mail 的行为方式:在屏幕截图中,我取消了顶部消息的标志,但它仍在显示。

图 8.11 Adobe Lightroom 中 collection 概念的映射困境:在显示两个集合的情况下,选择要删除的照片并不能确定将其从哪个集合中删除。

解决含糊不清的操作

用户的手势通常很容易解释。但有时它们含糊不清。当操作的参数取决于以前所做的选择时,这尤其容易发生。让我们来看一个这方面的例子。

collection 概念中,项目可以从集合中添加和删除,这些集合代表对项目的潜在重叠分类。使用集合的应用程序示例如下:Zotero,允许你将论文引用整理到集合中;像 Safari 这样的浏览器,提供书签集合;以及 Adobe Lightroom,让你定义照片或电影的集合。

使 collection 概念不同于 folder 概念的显著特征是,一个项目可以属于多个集合。映射操作 collection.add(i,c)(即把项目 i 添加到集合 c)通常很简单,而且(例如)可以通过让用户将项目拖到集合中来实现。

操作 collection.remove(i,c)(从集合 c 中移除项目 i)则更棘手。问题在于,在某些应用程序中,可以同时选择多个集合。这是一个重要特性,因为它允许用户在单个视图中查看属于多个集合的项目。在图 8.11 中,在 Adobe Lightroom 里选择了两个重叠的照片集合。

此时,指出 remove 操作的参数不再直截了当。你可能期望能够选择一个项目,然后(比如)按下删除按钮。但是如果所选项目同时属于两个集合,就不清楚要从哪一个集合中移除它。

图 8.12 映射部分样式对话框中的“none(无)”值。最近版本的 Word 和 InDesign(左下角)使用了扩展小部件,例如下拉菜单(选中条目后可作为文本编辑);较早版本的 Word 对字体有单独的选择器,它会填充可编辑文本字段(左上角);Apple Pages 09(右侧)使用复选框。

这是一个棘手的映射问题。当我最初起草本章时(2020 年末),如果你尝试执行此操作,Lightroom 会显示一条错误消息,通知你删除请求不明确。现在(2021 年 2 月),它只需将该项目从当前所选且它所属的所有集合中删除即可。

当标准小部件不够用时:输入“无”值

某些概念操作接收一个参数,该参数要么可以从一组值中获取,要么可以是“none(无)”,表示没有选择任何值。例如,在 format(格式)概念中,你可能有一个操作 set (p, v) ,将格式属性 p 设置为值 v;如果你设置了属性然后想要撤销该设置,你可能会将其值设置为 none

这种微妙之处在我第 4 章中提到的部分样式(partial styles)示例中出现。回顾一下,其思想是你可以定义一个 style,它仅指定某些格式属性的值。例如,你可能定义了一个称为 emphasis(强调)的字符样式,它将字体样式(font style)设置为 italic(斜体),但将所有其他字体属性(如大小和字体类型选择)保持不变。

现在考虑一下这如何映射到用户界面。我们不仅需要能够(比如)将字体样式设置为 italic,还需要能够*取消设置(unset)*它:也就是说,将字体样式值设置为 none(其默认值)。这与将其设置为 roman(正体)不同,后者会导致已经是斜体的字体被修改。

在提供部分样式的应用程序(如 Microsoft Word 和 Adobe InDesign)最新版本中,这是通过使用标准用户界面元素的扩展版本来实现的(图 8.12,左下方)。布尔复选框变为三态小部件(打开、关闭和未设置),下拉列表也得到了扩展,这样,除了从下拉菜单中选取一个值之外,你还可以编辑选定值(作为文本字段),从而将其删除(以取消设置)。这并不像看起来那么违反直觉,因为文本字段也可用于自动完成,这在菜单包含许多条目时很有帮助。

在这些程序的早期版本中,扩展的用户界面元素不可用,用户不得不忍受笨拙的界面或应用变通方法。在 Word 中,多年来,只有通过编写 Visual Basic 脚本才能取消设置部分样式;随后,一个复杂的对话框让你从列表中选择字体,然后它会填充到一个你可以编辑的单独文本字段中(图 8.12,左上角)。在 InDesign 中,一旦你设置了一个属性,就没有办法取消设置它,除了使用“重置为基础(reset to base)”操作,该操作会清除样式的所有属性。iWork ‘09 版本的 Apple Pages(图 8.12,右侧)通过向每个设置添加复选框来回避该问题;这种解决方案虽然干净清晰,且不需要花哨的界面元素,但被放弃了,大概是因为它占用了太多空间。

我们在本章中之前的所有示例将适用于任何用户界面框架。最后一个例子很有趣,因为它暴露了当代用户界面工具包的局限性,这些工具包通常不提供“取消设置”先前选定值的方法。

经验与实践

本章的一些经验:

  • 概念必须被映射到具体的用户界面上,操作映射到手势(如点击按钮),概念状态映射到各种类型的显示视图。
  • 用户界面设计原则是适用的,但概念有助于集中对映射的关注。在 Java 示例中,我们看到了在同一对话框中将安装和卸载混为一谈是如何导致混乱的,而更关注底层概念结构的映射会更加清晰。
  • 某些概念在本质上更复杂,需要在映射方面发挥更多巧思,有时甚至需要在用户界面中进行明确的解释。
  • 试图使使用户界面比底层概念更简单的做法可能会适得其反。在 Gmail 示例中,标签是附加在消息上的,但界面却将它们与对话关联,简化了视觉外观但损害了可用性。
  • 映射必须考虑到典型的使用模式,正如 Backblaze 和 Apple Mail 示例所暗示的,这可能比执行单个操作更复杂。
  • 尽管视觉界面具有表现力,但可能无法解决所有歧义,而且界面工具包可能会限制映射设计。

以及你现在可以应用的一些实践:

  • 在进行用户界面设计时,首先思考如何将每个概念单独映射。然后你可以扩展视野,考虑映射的概念在屏幕上如何组合,以及它们之间需要什么过渡和链接。
  • 当你为一个概念设计映射时,首先要确保每个操作在界面中都可用(当相关时),并且概念的状态能够以清晰的方式显示出来(当需要时)。
  • 根据概念的操作原理和用户最有可能使用可用操作的方式,检查你的界面设计。
  • 一如既往,基于概念的方法的一个关键优势在于,通过分离出通用功能,概念使得识别能启发你工作的前身变得更加容易。因此,在为一个概念设计映射时,看看该概念在其他应用程序中是如何映射的。