本文基于 wiki《 QT API Design Principles 》:
https://wiki.qt.io/Category:Developing_Qt::Documentation
1.常规陷阱(The Convenience Trap)
一种常见的错觉,需要实现的代码越少,API就越好。请记住,代码编写不止一次,但必须一遍又一遍地理解。例如:
//bad code
QSlider *slider = new QSlider(12, 18, 3, 13, Qt::Vertical, 0, "volume");
这条代码比下面的读起来要难得多(甚至写起来也更难):
//god code
QSlider *slider = new QSlider(Qt::Vertical);
slider->setRange(12, 18);
slider->setPageStep(3);
slider->setValue(13);
slider->setObjectName("volume");
2.布尔参数陷阱(The Boolean Parameter Trap)
布尔类型的参数总是带来无法阅读的代码。给现有的函数增加一个bool
型的参数几乎永远是一种错误的行为。
仍以Qt为例,repaint()
有一个bool
类型的可选参数用于指定背景是否被擦除。可以写出这样的代码:
//bad code
widget->repaint(false);
初学者很可能是这样理解的,『不要重新绘制!』,能有多少Qt用户真的知道下面3行是什么意思:
//bad code
widget->repaint();
widget->repaint(true);
widget->repaint(false);
更好的API设计应该是这样的:
//god code
widget->repaint();
widget->repaintWithoutErasing();
在Qt 4中,我们通过移除了重新绘制(repaint)而不擦除widget的能力来解决了此问题。Qt 4的双缓冲使这种特性被废弃。
还有更多这样的例子:(在现在的qt中已经找不到这样的代码了)
//bad code
widget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding, true);
textEdit->insert("Where's Waldo?", true, true, false);
QRegExp rx("moc_''''''.c??", false, true);
一个明显的解决方案是bool
类型改成枚举类型。我们在Qt 4的QString
中就是这么做的。对比效果如下:
str.replace("USER", user, false); // Qt 3
str.replace("USER", user, Qt::CaseInsensitive); // Qt 4
如果真的不知道怎么做,请记住千万不要把 BOOL 设计成函数参数。
3.案例
为了在实践中展示这些概念,我们将研究Qt 3的QProgressBar API,并将其与Qt 4 API进行比较。
在Qt 3:
class QProgressBar : public QWidget
{
…
public:
int totalSteps() const;
int progress() const;
const QString &progressString() const;
bool percentageVisible() const;
void setPercentageVisible(bool);
void setCenterIndicator(bool on);
bool centerIndicator() const;
void setIndicatorFollowsStyle(bool);
bool indicatorFollowsStyle() const;
public slots:
void reset();
virtual void setTotalSteps(int totalSteps);
virtual void setProgress(int progress);
void setProgress(int progress, int totalSteps);
protected:
virtual bool setIndicator(QString &progressStr, int progress, int totalSteps);
…
};
该API相当的复杂和不一致;例如,reset()
、setTotalSteps()
、setProgress()
是紧密联系的,但方法的命名并没明确地表达出来。
改善此API的关键是抓住QProgressBar
与Qt 4的QAbstractSpinBox
及其子类QSpinBox
、QSlider
、QDail
之间的相似性。怎么做?把progress
、totalSteps
替换为minimum
、maximum
和value
。增加一个valueChanged()
消息,再增加一个setRange()
函数。
进一步可以观察到progressString
、percentage
与indicator
其实是一回事,即是显示在进度条上的文本。通常这个文本是个百分比,但是可通过setIndicator()
设置为任何内容。以下是新的API:
virtual QString text() const;
void setTextVisible(bool visible);
bool isTextVisible() const;
默认情况下,显示文本是百分比指示器(percentage indicator),通过重写text()
方法来定制行为。
Qt 3的setCenterIndicator()
与setIndicatorFollowsStyle()
是两个影响对齐方式的函数。他们可被一个setAlignment()
函数代替:
void setAlignment(Qt::Alignment alignment);
如果开发者未调用setAlignment()
,那么对齐方式由风格决定。对于基于Motif
的风格,文字内容在中间显示;对于其他风格,在右侧显示。
下面是改善后的QProgressBar API
:
class QProgressBar : public QWidget
{
…
public:
void setMinimum(int minimum);
int minimum() const;
void setMaximum(int maximum);
int maximum() const;
void setRange(int minimum, int maximum);
int value() const;
virtual QString text() const;
void setTextVisible(bool visible);
bool isTextVisible() const;
Qt::Alignment alignment() const;
void setAlignment(Qt::Alignment alignment);
public slots:
void reset();
void setValue(int value);
signals:
void valueChanged(int value);
…
};