开发者也是用户 — 第一部分:构建更具可用性的 UI 与 API 的 5 个方针

在前一篇文章中,我们探讨了 UI 可用性与 API 可用性的重要性,并说明了 UI 可用性原则可以应用于 API。下面是前文链接:

开发者也是用户 - 简介 可用性 - 学于 UI,用于 API

在本文中,我们将具体讨论前 5 条可用性方针:

  1. 系统状态的可见性
  2. 让系统符合真实世界
  3. 为用户提供自由的操作方式
  4. 一致性与标准
  5. 预防错误的发生

1. 系统状态的可见性

系统应当在合理的时间,通过合适的反馈,让用户了解它正在做什么。

**UI:**当用户进行一项需要耗费较长时间的操作时,应告知用户操作的进度。例如,在加载图片时显示一个进度条,在上传下载文件时显示百分比。应当让用户知道正在让他们等待的是什么,需要花多长时间。

上图:告知用户当前状态。图片来源

**API:**API 应当提供某种可以查询当前状态的方式。例如,AnimatedVectorDrawable 类提供了一个方法来检查动画是否正在运行:

boolean isAnimationRunning = avd.isRunning();

API 可以采用回调机制来给出反馈,让 API 用户知道对象在何时改变了状态 —— 类似于动画开始与结束时的通知。例如,AnimatedVectorDrawable 对象可以 registering 一个 AnimationCallback 来完成上述操作。

2. 让系统符合真实世界

应用程序应当“说”用户的语言,使用用户熟悉的短语和概念,而不应该使用面向系统的术语。

上图:使用用户熟悉的概念。图片来源

类与方法的命名应符合用户的预期

**API:**当在一个新的 API 中查找类时,用户可能无从下手,因而依赖之前使用类似 API 的经验,或者依赖在 API 领域通用的观念。例如,当使用 Glide 或者 Picasso 下载并展示图片时,用户很可能会去查找名为“load”或“download”的方法。

3. 为用户提供自由的操作方式

为用户提供撤销操作的机会。

**UI:**某些用户发起的操作可能含有歧义,例如“删除”或“存档”邮件。此时应显示一条消息让用户确认,并允许用户撤销此操作。

上图:允许用户撤销当前操作。图片来源

API 应允许中断或重置操作,并能简单地将 API 恢复到正常状态

**API:**例如,Retrofit 提供了一个 Call#cancel 的方法,此方法会尝试取消飞行模式下的 call 调用,以及取消还未被 execute 执行的 call 调用,让其之后也不再会执行。此外,如果你在使用 NotificationManager,你会发现既可以创建通知也可以取消(cancel)通知。

4. 一致性与标准

你的应用程序的用户不应该去思考不同的文本、情景或者操作是否有着同样的意义。

**UI:**与你的 app 进行交互的用户在此之前已经通过与其它 app 交互得到了训练,他们会希望各个应用的可交互元素的样式与行为都相同。如果偏离了这些惯例,那么用户就会更容易出错。

因此,UI 需要与平台保持一致,并使用用户熟悉的 UI 控件,以方便用户快速识别并使用它们。此外,一致性应当贯穿你的整个应用。在 app 的不同界面中,使用相同的文字与图表来表示相同的东西。例如,在你的 app 中用户可以修改多个元素,那么请使用相同的修改图标。

上图:对话框应该与平台保持一致。图片来源

**API:**所有的 API 设计都应遵循一致性原则。

各个方法应保持命名的一致性

请参考下面的例子。假设我们有一个 interface 暴露了两个设置不同类型 observer 的方法:

public interface MyInterface {
    
    void registerContentObserver(ContentObserver observer);
    void addDataSetObserver(DataSetObserver observer);
}

使用它的用户可能会思考:register…Observeradd…Observer 究竟有什么区别呢?是否一个方法一次接受一个 observer,另一个方法一次可以接受多个 observer 呢?开发者要么去认真阅读文档,要么去查找 interface 的实现,来研究两个方法的行为是否相同。

private List<ContentObserver> contentObservers;
private List<DataSetObserver> dataSetObservers;
public void registerContentObserver(ContentObserver observer) {
    contentObservers.add(observer);
}
public void addDataSetObserver(DataSetObserver observer){
    dataSetObservers.add(observer);
}

因此,请为做同样事情的方法进行 相同的命名

可以在命名时考虑使用反义词,例如:get - set,add - remove,subscribe - unsubscribe,show - dismiss。

各个方法应保持参数顺序的一致性

在重载方法时,需要确保在新旧方法中都存在的参数的顺序保持一致。否则,你的 API 用户将要花更多的时间来理解重载与被重载方法的区别。

void setNotificationUri( ContentResolver cr,
                         Uri notifyUri);
void setNotificationUri( Uri notifyUri,
                         ContentResolver cr,
                         int userHandle);

避免在函数中使用连续的、同类型的参数

虽然在 Android Studio 中,使用连续的多个相同类型的参数是件简单的事情,但是这样做很容易导致参数顺序出错,并且很难找到这种错误。参数的顺序应当尽可能与参数的逻辑顺序一致。

当这些参数的类型都相同时,用户很容易犯错。例如上图中 county 和 country 就弄反了。

为了解决这种问题,你可以使用建造者模式,或者应用 Kotlin 的 命名参数(named parameters)

方法的参数应不大于 4 个

参数越多,意味着方法越复杂。用户需要理解每个参数在方法中起到的作用以及与其它参数的关系,也就是说每增加一个参数都会导致方法的复杂度呈指数形式增加。当一个方法的参数超过 4 个时,就可以考虑将其中一些参数封装在其它类中或使用构造器了。

返回值会影响方法的复杂度

当一个方法返回某个值时,开发者需要知道这个值代表着什么,如何存储它等。如果不需要用到这个值,那么它也不应当对方法的复杂度造成影响。

例如,当向数据库插入一个元素时,Room 既可以返回 Long 也可以返回 void。如用户需要使用返回值时,首先需要了解此返回值的意义,以及如何存储它。而在不需要返回值时,用户可以使用 void 类型方法。

@Insert
Long insertData(Data data);
@Insert
void insertData(Data data);

因此,你应当允许 API 用户自己决定是否需要返回值。如果你正在开发一个基于代码生成器的库,应该允许其生成返回多种可选类型的方法。

5. 预防错误的发生

创建防范于未然的设计。

**UI:**用户经常会一心多用,因此你应当防止用户在无意识下造成的错误,减少用户“翻车”的机会。比方说你可以在毁灭性操作前弹框要求确认,或者提供正确的缺省值。

比如,Google Photos 应用会弹出一个确认框来确保你删除相册不是误操作;而 Inbox 的“邮件稍后提醒”功能仅需一键操作。

上图:Google Photo 在毁灭性操作前弹出确认框;Inbox 在暂停收件操作时提供方便选择的缺省值。

API 应该引导用户正确地使用 API。尽可能使用缺省值。

API 应当易于使用,且能防止误用。通过提供缺省值可以帮助用户正确使用 API。例如,当创建 Room 数据库时,有一个缺省值可以确保在升级数据库版本时数据不丢失。由于数据库版本对用户来说是透明的,又因为升级时数据会保持,所以使用 Room 的应用程序对用户来说易用性更好。

与此同时,Room 也提供了一个方法 fallbackToDestructiveMigration 用于改变这种行为,如果没有提供迁移方法,那么在数据库版本改变时会销毁并重新创建数据库。


深入了解另外 5 条原则请访问: 让用户认知,而不是回忆 弹性、高效的使用方式 优雅、极简的设计 帮助用户认识、判断、改正错误 提供帮助与文档

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏社区的朋友们

HBase 学习分享

有些时候你可曾面对产品看似普通且合理的需求,例如:1、能否让网页活动拉取用户的游戏好友关系链从而更精准的推送Tips? 2、判断用户是否在所有大区都没有角色这类...

89100
来自专栏逆向技术

学习逆向知识之用于游戏外挂的实现.第三讲,通过游戏外挂.分析红色警戒金钱基址.以及确定基址小技巧.

                          分析红色警戒金钱基址.以及确定基址小技巧.

14910
来自专栏沈唁志

写给PHP开发者的五个建议

18840
来自专栏Linyb极客之路

分布式之redis复习精讲

博主的《分布式之消息队列复习精讲》得到了大家的好评,内心诚惶诚恐,想着再出一篇关于复习精讲的文章。但是还是要说明一下,复习精讲的文章偏面试准备,真正在开发过程中...

19630
来自专栏ImportSource

并发编程-多线程的好处

上一文:并发编程-并发的简史 如果线程使用得当,多线程可以降低你的开发和维护成本,而且还能改善复杂应用程序的性能。多线程让模仿人类工作方式以及交互变得简单,多线...

38060
来自专栏申龙斌的程序人生

零基础学编程004:集成开发环境IDE

几天前介绍了《用在线编程环境快速上手》学习Python等编程语言,这种教学环境中的例子都非常简单,你不需要在自己的电脑中安装任何的软件,就可以马上动手学习Pyt...

35450
来自专栏Zephery

谈谈个人网站的建立(四)—— 日志系统的建立

谈谈个人网站的建立(四)—— 日志系统的建立 欢迎访问我的网站http://www.wenzhihuai.com/ 。感谢,如果可以,希望能在GitHub上给个...

41540
来自专栏云计算教程系列

如何在Ubuntu 14.04上为IRC安装Lita Chat Bot

许多现代DevOps团队在聊天室周围建立了越来越多的基础设施。有很多聊天室,从商业选项(如HipChat和Slack)到DIY选项(如IRC或Jabber / ...

10710
来自专栏happyJared

用Python统计你的简书数据

  说来也巧,之前有一次无意间留意到简书好像没有做文章总阅读量的统计(准确的说法应该叫展示),刚好最近有时间,趁这个机会就用Python写了这么个功能,既是学习...

20510
来自专栏来自地球男人的部落格

python中scrapy点击按钮

最初遇到的问题的是在用scrapy爬取微博时需要按照指定关键字来爬取特定微博,主要还是解决需要输入关键字然后点击搜索按钮的问题。于是: 首先 找了scrapy的...

49470

扫码关注云+社区

领取腾讯云代金券