文档章节

API设计准则(转)

云贵高原
 云贵高原
发布于 2015/07/05 11:17
字数 2405
阅读 47
收藏 0

Six Characteristics of Good APIs

优良API的六个特征

An API is to the programmer what a GUI is to the end-user. The ‘P’ in API stands for “Programmer”, not “Program”, to highlight the fact that APIs are used by programmers, who are humans.
API于一个程序员正如GUI于使用者. API中的P表示程序员, 而不是程序, 以凸显出API是供程序员使用的, 而程序员也是人类.

In his Qt Quarterly 13 article about API design [doc.qt.nokia.com], Matthias tells us he believes that APIs should be minimal and complete, have clear and simple semantics, be intuitive, be easy to memorize, and lead to readable code.
在他的Qt Quarterly 13 article about API design[doc.qt.nokia.com]中, Matthias告诉我们他认为API应该小而全, 有清晰和简单的语义, 直观易记, 从而使代码易读.

  • Be minimal: A minimal API is one that has as few public members per class and as few classes as possible. This makes it easier to understand, remember, debug, and change the API.
  • : 一个小的API应该经可能的有较少的类和较少的类公共成员. 这让它易于理解, 记忆, 调试和更改
  • Be complete: A complete API means the expected functionality should be there. This can conflict with keeping it minimal. Also, if a member function is in the wrong class, many potential users of the function won’t find it.
  • : 一个全的API, 意味着预期的功能都具有. 这也许会和小冲突. 但是, 如果一个成员函数存在与一个错误的类里面, 许多该函数的潜在用户很难发现他们.
  • Have clear and simple semantics: As with other design work, you should apply the principle of least surprise. Make common tasks easy. Rare tasks should be possible but not the focus. Solve the specific problem; don’t make the solution overly general when this is not needed. (For example, QMimeSourceFactory in Qt 3 could have been called QImageLoader and have a different API.)
  • 清晰和简单的语义: 就像和其他的设计工作一样, 你应该运用最小惊奇定理. 让普通的任务容易完成. 稀有的任务也能完成但不是关注点. 解决特定的问题, 没必要的话不要让解决方法过于普适. (例如, Qt 3中的QMimeSourceFactory应该被定义为QImageLoader, 并且有不同的API)
  • Be intuitive: As with anything else on a computer, an API should be intuitive. Different experience and background leads to different perceptions on what is intuitive and what isn’t. An API is intuitive if a semi-experienced user gets away without reading the documentation, and if a programmer who doesn’t know the API can understand code written using it.
  • 直观: 与计算机里的其他东西一样, API应该是直观的. 不同的经验和背景导致对直观的不同理解. 如果一个稍有经验的人不需要查阅文档就能理解API, 或者是一个不了解API的程序员能明白用这个API写的代码, 那么这个API就是直观的.
  • Be easy to memorize: To make the API easy to remember, choose a consistent and precise naming convention. Use recognizable patterns and concepts, and avoid abbreviations.
  • 易记: 为了让API易记, 选择一个统一且精确的命名规范. 使用易记的模式和概念, 避免缩写
  • Lead to readable code: Code is written once, but read (and debugged and changed) many times. Readable code may sometimes take longer to write, but saves time throughout the product’s life cycle.
  • 使代码易读: 代码只用写一次, 但是会被阅读(或者调试, 修改)很多次. 易读的代码需要较长的时间去书写, 但是在产品的生命周期里节省了可观的时间

Finally, keep in mind that different kinds of users will use different parts of the API. While simply using an instance of a Qt class should be intuitive, it’s reasonable to expect the user to read the documentation before attempting to subclass it.
最后, 牢记在心, 不同的用户会使用API的不同部分. 虽然简单的使用一个Qt类的实例是很直观的, 但是用户应该在继承类之前阅读文档.

Static Polymorphism

静态多态性

Similar classes should have a similar API. This can be done using inheritance where it makes sense — that is, when run-time polymorphism is used. But polymorphism also happens at design time. For example, if you exchange a QProgressBar with a QSlider, or a QString with a QByteArray, you’ll find that the similarity of APIs makes this replacement very easy. This is what we call “static polymorphism”.
相似的类应该具有相似的API. 这可以通过在需要的地方使用继承来做到 — 那就是说, 当用到运行时多态的时候. 但是多态也会出现在设计的时候. 例如, 如果一个QProgressBar去替换QSlider, 或者一个QString去替换QByteArray, 你会发现相似的API会让替换很容易. 这就是我们说的静态多态性.

Static polymorphism also makes it easier to memorize APIs and programming patterns. As a consequence, a similar API for a set of related classes is sometimes better than perfect individual APIs for each class.
静态多态性也让API和编程模式易记. 这样的结果就是, 一个相似的API对应着一些相关类的集合, 有些时候比每个类的完美的独立的API好得多.

In general, in Qt, we prefer to rely on static polymorphism than on actual inheritance when there’s no compelling reason to do otherwise. This keeps the number of public classes in Qt down and makes it easier for new Qt users to find their way around in the documentation.
通常来说, 在Qt里, 继承上我们选择依赖静态多态性, 当没有无法控制的原因让我们另外换一种做法的时候. 这让Qt里类的数量减少了, 并且让新的Qt用户更容易在文档里找到他们.

Good: QDialogButtonBox and QMessageBox have similar APIs for dealing with buttons (addButton(), setStandardButtons(), etc.), without publicly inheriting from some “QAbstractButtonBox” class.
: QDialogButtonBox和QMessageBox在处理按钮时有相似的API(addButton(), setStandardButtons(), 等等), 并没有从类似于“QAbstractButtonBox”这样的类继承而来.

Bad: QAbstractSocket is inherited both by QTcpSocket and QUdpSocket, two classes with very different modes of interaction. Nobody seems to have ever used (or been able to use) a QAbstractSocket pointer in a generic and useful way.
: QAbstractSocket继承于QTcpSocket和QUdpSocket, 这两个类有着非常不同的交互. 貌似没人能通用且有效的使用QAbstractSocket指针.

Dubious: QBoxLayout is the base class of QHBoxLayout and QVBoxLayout. Advantage: Can use a QBoxLayout and call setOrientation() in a toolbar to make it horizontal/vertical. Disadvantages: One extra class, and possibility for users to write ((QBoxLayout *)hbox)->setOrientation(Qt::Vertical), which makes little sense.
不好不坏的: QBoxLayout是QHBoxLayout和QVBoxLayout的基类. 好处: 可以使用 QBoxLayout 并且在toolbar中调用tsetOrientation()来让它横向/竖向. 坏处: 多了一个类, 可能用户会写出((QBoxLayout *)hbox)->setOrientation(Qt::Vertical)这样的代码, 虽然有一些意义.

Property-Based APIs

基于属性的API

Newer Qt classes tend to have a “property-based API”. E.g.:
新的Qt类倾向于”基于属性的API”, 例如:

1
2
3
4
QTimer timer; timer.setInterval(1000); timer.setSingleShot(true); timer.start();

By property, we mean any conceptual attribute that’s part of the object’s state — whether or not it’s an actual Q_PROPERTY. When practicable, users should be allowed to set the properties in any order; i.e., the properties should be orthogonal. For example, the preceding code could be written.
提到属性, 我们是指任何属于对象状态的概念性属性 – 不管其是不是Q_PROPERTY. 当可行的时候, 用户应该能以任何顺序设置属性. 那就是说, 属性是正交的. 例如, 前面的代码也可以这样:

1
2
3
4
QTimer timer; timer.setSingleShot(true); timer.setInterval(1000); timer.start();

For convenience, we can also write timer.start(1000).
方便起见, 我们也可以写 timer.start(1000);

Similarly, for QRegExp, we have
类似的, 对于QRegExp, 我们有:

1
2
3
4
QRegExp regExp; regExp.setCaseSensitive(Qt::CaseInsensitive); regExp.setPattern("***.*"); regExp.setPatternSyntax(Qt::WildcardSyntax);

To implement this type of API, it pays off to construct the underlying object lazily. E.g. in QRegExp’s case, it would be premature to compile the “***.*” pattern in setPattern() without knowing what the pattern syntax will be.
为了实现这种类型的API, 延迟创建底层对象是值得的. 比如, 在QRegExp的例子里, 可以提前在Pattern里编译”***.*”模式, 而不需要知道模式语法是什么.

Properties often cascade; in that case, we must proceed carefully. Consider the “default icon size” provided by the current style vs. the “iconSize” property of QToolButton:
属性是可以层叠的. 在这种案例里, 我们必须小心处理. 设想当前样式提供的”默认图标大小”和QToolButton的”iconSize”属性:

1
2
3
4
5
6
7
toolButton->iconSize(); // returns the default for the current style toolButton->setStyle(otherStyle); toolButton->iconSize(); // returns the default for otherStyle toolButton->setIconSize(QSize(52, 52)); toolButton->iconSize(); // returns (52, 52) toolButton->setStyle(yetAnotherStyle); toolButton->iconSize(); // returns (52, 52)

Notice that once we set iconSize, it stays set; changing the current style doesn’t change a thing. This is good. Sometimes, it’s useful to be able to reset a property. Then there are two approaches:
注意一但我们设置了iconSize, 他就一直被设置了. 改变当前样式对它并没有影响. 这很好. 有时候, 重置一个属性是非常有用的. 有两种方法可以做到:

  • pass a special value (such as QSize(), -1, or Qt::Alignment(0)) to mean “reset”
  • 传递一个特殊的值(例如QSize(), -1, 或者Qt::Alignment(0))来表示重置
  • have an explicit resetFoo() or unsetFoo() function
  • 有一个显示的resetFoo()或者unsetFoo()的函数

For iconSize, it would be enough to make QSize() (i.e., QSize(-1, -1)) mean “reset”.
对于iconSize, 用QSize() (例如QSize(-1,-1)) 来重置就足够了.

In some cases, getters return something different than what was set. E.g. if you call widget->setEnabled(true), you might still get widget->isEnabled() return false, if the parent is disabled. This is OK, because that’s usually what we want to check (a widget whose parent is disabled should be grayed out too and behave as if it were disabled itself, at the same time as it remembers that deep inside, it really is “enabled” and waiting for its parent to become enabled again), but must be documented properly.
在有些案例中, getter的返回和设置的值不同. 比如, 如果你调用widget->setEnabled(true), 你仍然可能会从widget->isEnabled()那里得到false, 如果widget的父亲是被禁用了的. 这没什么, 因为我们通常想去检查(一个widget的父亲被禁止了, 它也应该变灰并且好像是它自己禁止的, 同时, 当其被激活的时候, 它会等待它的父亲被激活), 但是必须在文档里写清楚.

 

来源: http://lyxint.com/archives/256

本文转载自:http://lyxint.com/archives/256

云贵高原

云贵高原

粉丝 83
博文 38
码字总数 21429
作品 12
海淀
技术主管
私信 提问
系统架构师-基础到企业应用架构-系统设计规范与原则[上篇]

一、上章回顾 在上篇中我们讲解了几类UML2.0语言新推出的建模图形,总体来说通过这些图形能更详细的将某类信息表达出来。在这里我们简单回顾上篇讲解的内容。 上图中已经简单介绍了上章讲述的...

wbf961127
2017/11/12
0
0
从交互角度聊聊四类弹框的四大设计准则

今天从交互角度给大家讲讲弹框的设计,包括系统提示弹框,操作引导弹框,信息反馈弹框和广告弹框,每一种都总结了4个相关的设计准则,案例丰富,深入浅出,建议阅读。 本来传统意义上的弹框一...

jongde
2016/08/22
42
0
Single Threaded Execution

Single Threaded Execution 这个设计模式是多线程设计时候最基础的一个准则,对于临界区的值只允许一个线程进行修改。定义临界区还是使用synchronized来定义。(虽然jdk 7出了很多并发包api...

年少爱追梦
2016/04/19
34
0
组件接口(API)设计指南[1]-要考虑的问题

*返回目录阅读其他章节: http://blog.csdn.net/cuibo1123/article/details/39894477 在开发程序时,我会经常设计一些可重复使用的API,这些组件通常是针对IOS的(有时也会运行在MacOS上),并且...

cuibo1123
2014/10/08
0
0
深入理解redux之reducer为什么是纯函数

reducer是redux的三个核心概念之一,它指定了应用状态的变化如何响应 actions 并发送到 store,需要由开发人员自己定义,提起reducer,最常想到的一个准则是reducer要是纯函数,那么这其中是...

LT_bear
05/12
0
0

没有更多内容

加载失败,请刷新页面

加载更多

使用递归打印乘法表

一般我们在学for循环的时候都会去打印九九乘法表,但是如果是用递归的方式打印的话,应该怎么做呢? 下面讲解一下用递归打印九九乘法表的思路: 其实我们在用for循环打印乘法表的时候,用的是...

INEVITABLE
13分钟前
4
0
sql 练习

创建需要的4张表 首先创建student、course、score、teacher这四张表。 student表 创建student表 CREATE TABLE IF NOT EXISTS student(sno TINYINT UNSIGNED NOT NULL,sname VARCHAR(20......

Garphy
13分钟前
5
0
vSphere的两种虚拟交换机

VMware vSphere 6.7中支持两种虚拟交换机: 1、标准交换机,VSS - Virtual Standard Switch 2、分布式交换机,VDS - Virtual Distributed Switch VSS与ESXi主机一一对应,即一个VSS只能部署在...

大别阿郎
16分钟前
4
0
webGL和three.js的关系

本文转载于:专业的前端网站➤webGL和three.js的关系 如今浏览器的功能越来越强大,而且这些功能可能通过JavaScript直接调用。你可以用HTML5标签轻松地添加音频和视频,而且可以在HTML5画布上...

前端老手
43分钟前
7
0
Spring如何实现AOP,请不要再说cglib了!

1. 从注解入手找到对应核心类 最近工作中我都是基于注解实现AOP功能,常用的开启AOP的注解是@EnableAspectJAutoProxy,我们就从它入手。 上面的动图的流程的步骤就是: @EnableAspectJAutoPr...

温安适
46分钟前
38
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部