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应该小而全, 有清晰和简单的语义, 直观易记, 从而使代码易读.
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类的实例是很直观的, 但是用户应该在继承类之前阅读文档.
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)这样的代码, 虽然有一些意义.
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, 他就一直被设置了. 改变当前样式对它并没有影响. 这很好. 有时候, 重置一个属性是非常有用的. 有两种方法可以做到:
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