Saleri

The Essence of Software.Chapter.02.发现概念

最后一次修改于

一个软件产品——从运行在手机上的最小应用,到最大的企业级系统——是由*概念(concepts)*组成的,每一个概念都是一个自包含的功能单元。尽管概念为了更大的目标协同工作,它们仍然可以被独立地理解。如果一个应用就像一种化学混合物,概念就像分子:尽管它们结合在一起,但无论在哪里发现它们,其属性和行为都是相似的。

你已经熟悉许多概念,并知道如何与它们交互。你知道如何打一个电话(call)或预订(reservation)餐厅,如何在社交媒体论坛中为一个评论点赞(upvote),以及如何在文件夹(folder)中组织文件。一个概念令人熟悉且设计良好的应用,只要其概念在用户界面中被忠实地表达并被正确地编程,就很可能易于使用。相反,一个概念复杂或笨重的应用不太可能运行良好,无论表现形式多么花哨,算法多么聪明。

由于概念没有可见的形式,它们相当抽象,这也许是它们直到现在才成为关注焦点的原因。我希望在本书的过程中说服你,通过从概念的角度思考,通过“看透”用户界面发现其背后的概念,你将能够更深入地理解软件——更有效地使用它,更好地设计它,更准确地诊断缺陷,并以更专注和自信的态度构想新产品。

在某件东西坏掉之前,我们通常不会意识到它是如何工作的。你可能认为你的热水器就像变魔术一样源源不断地产生热水。但是到了某个时刻,你家里的某个人洗澡洗得太多了,你的淋浴变冷了。那时你可能会了解到你的热水器有一个容量有限的储水箱(storage tank)。

同样,要了解概念,我们需要看看当它们出错时会发生什么。因此,本书的大部分内容将涉及在看似不太可能的场景中失效的概念示例,或者那些事实证明比你预期的要难理解得多的概念。在本章中,我们将看到我们的第一批概念示例,以及它们如何解释一些意想不到(并且令人惊讶地复杂)的行为。

但是不要退缩,或者得出这样的结论:概念的想法本身是晦涩复杂的。相反,这个想法直截了当,采用它将有助于你设计出比我们今天使用的许多软件更简单、更强大的软件。

第一个例子:令人困惑的备份

为了保护我的工作免受磁盘损坏和意外删除的影响,我使用了一个名为Backblaze的出色备份实用程序,它将我的文件复制到云端,并允许我在需要时恢复旧版本。它在后台持续且隐形地运行,关注我计算机中的每一个文件,如果文件发生变化,它就将其复制到云端。

最近,我剪辑了一段视频,并希望确保在为了节省空间而删除旧版本之前,新版本已经备份。我检查了备份状态,它说:“您的备份已更新至:今天下午1:05。”由于我是在下午1:05之前创建的新视频,我以为它已经备份了。为了以防万一,我尝试从云端恢复它。但是它不在那里。

我联系了技术支持,他们向我解释说,文件并不是完全连续备份的。有一个定期扫描会编译一个新建或修改文件的列表;当下次备份运行时,只有该列表上的文件会被上传。因此,在扫描和备份之间进行的任何更改都会被遗漏,直到它们在下一次扫描中被发现。

他们告诉我,我可以通过在按住Option键的同时单击“立即备份(Backup Now)”按钮来强制重新扫描。我听从了这个建议,并等待扫描和随后的备份完成。现在,我的新视频肯定会出现在恢复列表上了!但运气不佳。在这一点上,我完全感到困惑,并寻求了更多帮助。原来我的视频已经上传了,但只是上传到了一个特殊的“暂存(staging)”区域,文件每隔几个小时才会从那里移动到恢复区域。

我的问题在于我误解了Backblaze的核心备份(backup)概念。我原本以为文件是持续上传的,并直接移动到恢复区域(图2.1,左图)。实际上,只有上次扫描产生的列表上的文件才会被上传,甚至即便如此,在它们稍后从上传目的地转移到恢复区域之前,它们仍然不可用(图2.1,右图)。

图 2.1 Backblaze的备份概念。左图是我所假设的:(1)我对一个文件进行更改;(2)当备份运行时,文件被复制到云端;(3)然后我就可以恢复它。右图是实际发生的情况:(1)我对一个文件进行更改;(2)一次扫描运行并将该文件添加到待备份的文件列表中;(3)备份运行,只将那些在上次扫描中添加的文件复制到云端;(4)定期地,备份的文件被移动到一个云端位置;(5)它们可以从那里被恢复。

这是一个小例子,但它说明了我的核心观点。我并不是在表态Backblaze的设计是否有缺陷;不过,我怀疑它是可以改进的(参见第8章的建议)。可以肯定的是,如果我从字面上理解了备份消息并且不知道扫描这件事,我可能会丢失一些关键文件。

主张的是,任何关于这种设计的讨论都必须围绕着基本概念展开,在这种情况下的备份(backup)概念,并评估它所体现的行为模式是否适合其目的。用户界面也很重要,但仅在其通过向用户表示应用程序的概念来为这些概念服务的程度上重要。如果我们想让软件更易于使用,概念是我们必须开始的地方。

Dropbox的错觉

我的一个朋友她的笔记本电脑空间快用完了。所以她很聪明地按大小对文件进行了排序,并向下查看列表,看看是否有一些她可以删除的庞大且陌生的文件。她找出了十几个这样的文件,并继续将它们删除了。几分钟后,她接到了老板打来的惊慌失措的电话,问那些包含一个重要工作项目数据的大文件发生了什么事。

哪里出了问题?要回答这个问题,我们需要理解一个流行的文件共享实用程序Dropbox的一些关键概念。Dropbox允许多个用户查看共享的文件和文件夹集合,并协作更新它们。为了维持这种错觉,Dropbox将一个用户所做的更改传播到其他用户看到的版本中。问题将是:什么类型的更改会被传播?在什么条件下?

Ava是一名派对策划师,她使用Dropbox与她的客户进行协调。她正在为Bella策划派对,所以她创建了一个名为Bella Party的文件夹并与Bella共享(图2.2)。无论Ava在文件夹中放什么,Bella现在都能看到。事实上,共享是对称的;无论Bella放入什么,Ava也能看到,而且其中一个人所做的任何更改,另一个人也会看到完全相同的更改。所以就好像只有一个文件夹副本可供Ava和Bella共同处理。

实际上,事情并没有那么简单,因为并非其中一人所做的所有更改都会被另一人看到。也许Bella不想让文件夹被称为Bella Party——毕竟,这是她的派对!所以她给这个文件夹起了一个新名字My Party。问题是:Ava现在看到了什么?对她来说,名字也变了吗?

只有两种可能性。要么Bella的操作也会改变Ava看到的名字,在这种情况下,就只有一个共享的文件名;要么不会改变,在这种情况下,同一个文件夹有两个名字,一个Ava使用,一个Bella使用。

那么究竟发生了什么?事实证明,两种结果都是可能的,这取决于文件夹是如何共享的。在这种情况下,Ava明确地将文件夹与Bella共享,Bella的重命名操作将只能被Bella看到,Ava不会看到这个改变。但假设Ava在Bella Party里面创建了另一个名为Bella Plan的文件夹(图2.3顶部)。这第二个文件夹现在是隐式地共享的(凭借其包含的文件夹被共享)。现在,如果Bella将Bella Plan重命名为My Plan,比方说,那么Ava看到这一更改。

你可能会认为这种行为的可变性是偶然的,这是在Dropbox演变过程中产生的一些任意选择的结果。或者你可能会认为它是错误的证据。事实上,这些都不是真的。这种表面上的设计怪癖是Dropbox设计的一个基本方面的直接后果。

图 2.2 在Dropbox中共享一个文件夹。Ava (AA) 已经将名为Bella Party的文件夹与Bella (BB) 共享。如果Bella现在更改了文件夹的名称,Ava会看到这种更改吗?

在我准确解释正在发生的事情之前,让我们考虑另一个问题。如果Bella删除了一个文件夹会发生什么?Ava的副本也会被删除吗?同样,这取决于上下文。如果Bella删除Bella Party,只有她的副本会消失;但如果她删除Bella Plan,Ava也会丢失它。在两种情况下,Dropbox确实给出了不同的消息(图2.3),其中一种消息更充分地解释了将要发生的事情。但是,奇怪的是,附加信息在第一种情况下给出,而不是在删除导致文件永久丢失的第二种情况下给出。

现在我们对朋友的经历有了个解释。她的老板原本只想和她共享一个文件,却共享了整个文件夹。当我朋友删除她没有使用的文件时,她是从共享文件夹中删除它们的——从而为每个人(包括她的老板)删除了它们。

图 2.3 Dropbox的文件夹删除消息。文件夹Bella Party已被共享(顶部)。如果该文件夹被删除,消息(中部)通知您该删除操作不会传播给其他用户。如果包含在其中的文件夹Bella Plan被删除,会出现一条不同的消息(底部),令人惊讶的是没有警告其他用户也会丢失该文件夹。

解释Dropbox

要查看在这些共享场景中发生了什么,首先阐明我们的期望是什么会有所帮助。一种简单而熟悉的命名设计会将名称视为贴在物理对象上的不干胶标签——比如猫项圈或车牌——每个对象最多只有一个标签(图2.4,左图)。我们可以称这种方法为“名称即元数据”,它是更通用的元数据(metadata)概念的一个实例,其中描述对象的数据(如照片的标题或说明)可以附加到对象上。

图 2.4 Dropbox中文件夹的两种可能概念:在元数据概念中(左),名称是附加到文件夹上的标签;在unix文件夹概念中(右),名称属于父文件夹内的条目。

关于删除,最简单的设计是在删除文件或文件夹时让它消失。我们可以称这种方法(使用术语)为“像魔法般消失(poof)的删除”方法:你点击删除,“噗!”——它就不见了。这里潜在的概念是——可以存储物品池,并有向池中添加或移除物品的操作——它是如此基础和熟悉,以至于它没有名字。在这个设计中,我们期望有一个单独的共享(sharing)概念,带有取消共享(unshare)操作,以便你可以删除别人与你共享的文件或文件夹,并在不删除他们副本的情况下释放自己帐户中的空间。

所有这些理解——名称是元数据,而删除只是从池中移除项目——都是错误的(至少对于Dropbox来说)。这些理解背后的概念本身很好;它们只是并非Dropbox使用的概念。如果你对一个软件应用持有错误的概念模型,你可能会侥幸逃脱一段时间。我们已经看到,在某些场景中,这些解释可以成功运作。但在另一些场景中,它们将失败,也许会带来灾难性的后果。

Dropbox实际使用的概念非常不同(图2.4,右图)。当一个项目位于文件夹中时,该项目的名称不属于该项目本身,而是属于包含它的文件夹。可以把文件夹想象成集合标签(tags),每个标签包含一个项目(文件或文件夹)的名称以及指向它的链接。这个概念,我将称之为unix folder,并非Dropbox发明的,而是顾名思义,借鉴自Unix。

看看图2.4(右图)中的图表。Ava和Bella各自都有自己的顶级Dropbox文件夹,这两个文件夹针对名为Bella Party的单一共享文件夹有着独立的条目。当Bella重命名Bella Party时,这改变了她自己的Dropbox文件夹中的条目,而Ava文件夹中的条目并未改变。

相比之下,保存第二级共享文件夹Bella Plan名称的条目只有一个,它属于名为Bella Party的单一共享父文件夹。因为该文件夹只有一个条目——Ava和Bella看到的都是同一个条目——当Bella重命名该文件夹时,她正在改变她们共享文件夹中的那个唯一条目,因此Ava也看到了这个改变。

使用这个相同的unix folder概念,我们现在可以解释删除行为。删除并不本身移除文件夹;它移除其条目。所以如果Bella删除文件夹Bella Party,她从她自己的文件夹中删除了该条目,而Ava的视图不变。但如果Bella删除了Bella Plan,她就从共享文件夹中删除了该条目,此时Ava也无法访问被删除的文件夹。

这是一种什么缺陷?

此时,你可能会对自己说:好吧,这都很明显。我知道Dropbox表现得像这样,我一点也不惊讶。Dropbox没有错,不明白这一点的人不应该使用它。但是如果你这样想,我敢肯定你只是少数读者。我们将这个场景展示给MIT计算机科学的学生,发现他们中的许多人,甚至是经常使用Dropbox的人,都感到困惑。

即使你明白了所有这些微妙之处,我也会争辩说仍然存在问题。这两种情况之间的区别——作为操作对象的文件夹是在顶层被共享的,还是属于另一个本身被共享的文件夹——在用户界面中不容易察觉,因此不得不弄清楚自己处于哪种情况是一种持续的烦恼。

此外,这种相当武断的区分应该决定行为似乎是不合理的。为什么我只能给顶级文件夹赋予我自己设定的名字?为什么我不能给我被共享的所有文件夹赋予私有名称?或者反过来,如果为我们双方重命名文件夹是我们共享工作的一部分,为什么我只能对某些文件夹进行重命名而对其他文件夹不能?

图 2.5 交互设计的层级。

那么,假设这些场景确实是Dropbox存在缺陷的证据,我们可以问:这是一种什么缺陷?它肯定不是一个bug;Dropbox这么多年一直都是这么运作的。我们可能会想这是否是用户界面的一个缺陷。这似乎也不太合理。当然,当您进行的更改会影响其他用户时,Dropbox可能会提供更丰富的信息。但这可能只是被视为额外的复杂性,而且经验表明,如果警告信息出现得太频繁,用户就会忽略它们。

真正的问题埋藏得更深。它存在于文件和文件夹如何命名,以及这些名称如何与文件夹及其内容之间的包含关系相关联的本质中。这就是我所说的*概念(conceptual)*设计问题。缺陷在于Dropbox开发人员将某些铭记于心的概念忠实地实现了。但是这些概念,最起码不与大多数用户心中的概念相一致。在最坏的情况下,这些概念并不符合用户的目的。

设计的层级

为了客观看待概念设计,将软件设计分为多个层级是有帮助的,如图2.5所示。这种分类是我自己的,但它与以前提出的方案相似。

设计的第一个层级,即物理层级(physical level),是关于制成品的物理属性。即使软件的界面只是一个对触摸敏感的玻璃,它也有这样的属性,尽管它们可能受到限制。在这一层级,设计人员必须考虑到人类的身体能力。这是出现可访问性问题的地方,当设计人员考虑有视力障碍、色盲或听力障碍的用户可能会如何交互时。

图 2.6 物理层级的设计问题,以及应用菲茨定律(Fitts's Law)的经典案例。哪种菜单放置位置能更方便地访问:macOS布局(在左侧),应用程序的菜单栏总是出现在桌面的顶部;还是Windows布局(在右侧),菜单栏是应用程序窗口的一部分?

人类共同的特征决定了某些设计原则。例如,我们有限的视觉采样率会导致知觉融合(perceptual fusion),使得我们很难区分彼此之间发生在大约30毫秒内的事件,这一事实表明,每秒30帧足以让电影看起来流畅。它还告诉我们,耗时远超30毫秒的系统反应会被用户感知为延迟,应该避免,或者给出进度条,如果更长的话,给个中止的机会。同样,菲茨定律(Fitts’s Law)预测了用户将指点设备移动到目标所需的时间,并解释了为什么菜单栏应该放置在屏幕顶部(如在Macintosh桌面中),而不是在应用程序窗口中(如在Windows中)(图2.6)。

设计的第二个层级是语言层级(linguistic level)。这一层级关注使用语言来传达软件提供的行为,帮助用户导航软件,了解哪些操作可用以及它们将产生什么影响,发生了什么事情等等。虽然物理层级的设计必须尊重其用户物理特征的差异性,但这一层级的设计必须尊重文化和语言的差异。

显然,应用程序上的按钮标签和工具提示会根据它的目标受众是说英语的还是说意大利语的而有所不同。(我记得小时候去意大利度假时,在一个惨痛的经历中了解到,标有calda的水龙头不是冷水。)设计师也必须意识到文化差异。在欧洲,一个内部为白色的红色圆圈路标意味着不允许任何车辆通行;大多数美国司机将无法理解这一点(并且可能期望通过一条红色的对角线来表达禁令)。

图 2.7 语言层面的设计问题:在谷歌应用程序中对图标的不一致解读以及新图标如何修复了这个问题。左侧是用于打开应用程序和切换到网格视图的原始图标;右侧是执行相同操作的新图标,现在已加以区分。

当用户界面设计师谈论需要一致性时,他们通常指的是在这一层面使用语言。一致性包括确保在整个界面中以相同的方式使用相同的单词——例如,用于存放文件的容器在一个地方不被称为“文件夹(folders)”,在另一个地方又被称为“目录(directories)”——并且图标必须系统地使用。图2.7显示了Google如何因为将两个几乎相同的图标用于不同的功能而违反了这一原则(但几年后修复了该问题)。这两个图标描绘的都是黑色方块的阵列,但其中一个是打开Google应用程序菜单,另一个是切换到文件网格视图。

设计的第三个也就是最高层级是概念层级(conceptual level)。它涉及设计背后的行为:由用户(和软件本身)执行的操作以及它们对底层结构产生的影响。与语言层面形成对比的是,概念层面不是关于沟通或文化的,即便(正如我们将在第10章中看到的)对某个概念的先验知识会使它更容易学习和使用。

在编程中,*抽象(abstraction)表示(representation)*之间有着熟悉且重要的区别。抽象捕捉到了编程思想的本质,并且可以表示为可观察行为的规范。表示是将该本质在代码中的实现。

以同样的方式,用户交互具有抽象和表示。抽象就是概念——结构和行为的本质——它是概念层级的主题。表示则是概念在用户界面中的实现,包含所有物理和语言上的细节,并且是较低层级设计的主题。

正如单个编程抽象可以具有不同的表示一样,一个概念可以在不同的用户界面中实现。正如程序员首先考虑抽象,然后才考虑表示一样,设计师在进入较低层级之前会在概念层级进行思考。到目前为止,设计师还没有一种方法在没有在具体的用户界面中表现出来的情况下表达概念设计理念。本书的目的是表明,这种理念可以被直接表达,在做出表现形式选择之前并且独立于其表达。

心理模型与概念设计

在大多数软件应用中,用户遇到的困难很少是因为应用功能太少或太多。更常见的问题是用户无法有效利用实际存在的功能。这可能是因为用户没有主动去寻找功能,并假设它们不存在。

但更常见的情况是,用户知道有这些功能,但就是无法成功使用它们。这最常见的原因是用户有一个错误的心理模型(mental model)——也就是说,与软件设计者和实现者的心理模型不兼容。虽然研究已经反复表明(且毫不意外),用户对其使用的设备往往有着模糊、不完整甚至不一致的模型,但他们确实会形成一些类似于(至少在形式上,如果不是在每一个细节上)设计者头脑中的概念的想法。

但是,当用户的心理模型严重错误时,他们就不太可能有效地使用其功能,正如我们在Dropbox的例子中看到的那样。他们可能会遭受严重损失,或者因为害怕犯下昂贵的错误而过于紧张,以至于他们只使用了极小部分功能。

解决这个问题的一个糟糕的方法是尝试教育用户。这通常是行不通的,因为大多数用户会抵制花时间学习如何使用一款应用;他们期望(并非不合理)在使用的过程中学会它。更好的解决方案是设计软件的概念,使它们简单、灵活并非常适合用户的需求,并设计用户界面将这些概念传达给用户。

这些概念本身就成为了用户预期心理模型和提供给程序员的规范的基础。用户界面设计师的任务是投射出一个如可用性研究员Don Norman所称的“系统形象(system image)”,这个形象忠实地对应概念模型,从而让用户获得与其相一致的心理模型。

图 2.8 概念的核心作用(左)使用户的心理模型(右上)和体现在代码中的开发人员的设计模型(右下)对齐。通过将概念小心地映射到用户界面(右中),不仅完全支持这些概念,而且还将它们隐式地传达给了用户。

图2.8描述了这一点。顶端是用户;底部是程序员编写的代码;在两者之间的是用户界面。为了使软件获得成功,我们需要了解用户(通过调查其需求、工作环境和心理特征);确保代码满足其规范(通过测试、审查和验证);并精心打造一个可用的界面。但最重要的是,让用户头脑中的模型与程序员头脑中的模型保持一致,而这正是通过明确设计出那些由用户和程序员共同拥有的、且在用户界面中被清晰传达的概念来实现的。

经验教训与实践

本章的一些经验教训:

  • 软件应用中主要的可用性问题通常可以追溯到其底层概念。例如,在Dropbox中,关于删除是否会影响其他用户的困惑可以通过Dropbox采用了源自Unix的概念来解释。
  • 软件设计在三个层级进行:物理层级,它涉及设计按钮、布局、手势等,以匹配人类用户的物理和认知能力;语言层级,涉及设计图标、消息和术语来与用户沟通;概念层级,涉及将底层行为设计为一组概念。下面两个层级关注的是在用户界面中表示概念。
  • 对于用户来说,拥有正确的心理模型对于可用性至关重要。为了确保这一点,我们需要设计简单明了的概念,并将概念映射到用户界面,以便概念易于理解和使用。

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

  • 拿一款你觉得难以使用的应用。问自己涉及了什么概念,并检查你关于它们如何工作的假设是否符合实际行为。如果不符,你能找到能更准确解释其行为的不同概念吗?
  • 作为一款应用的设计师,考虑用户觉得最难使用(或最容易误用)的功能。你能指出一两个负有责任的概念吗?
  • 设计时,了解你正在哪个层级工作。从概念层级开始,然后向下移动。在较低层级上画出概念草图可以帮助你更直观地掌握它们,但在你对概念有了清晰的认知之前,抵制住打磨物理界面的诱惑(例如,担心字体、颜色和布局细节)。
  • 当你听到对应用的抱怨集中在物理或语言方面时,问问潜在的问题是否反而可能存在于概念层级。