aKademy 2004

Designing Qt-style APIs

Matthias Ettrich


APIs


Implications

APIs should:


Minimal

Rationale:

Note: overloads are conceptually cheaper than other members


Complete

Rationale: if a member function is misplaced in a class, this can lead to two effects:

  1. it leads to even more functions (nasty things tend to grow on their own)
  2. many potential users of the function won't find it.


Clear and simple semantics


Intuitive

Intuition is based on experience, different experience and background leads to different perception on what is and is not intuitive.

Definition:

A Qt-style API is intuitive...


Easy to memorize


Guidance to readable code

Rationale: Code is written once, but read/debugged/understood many many times.

=> Readable code may sometimes take longer to write, but safes time throughout the code lifecycle.


Mindset: Picture your target user


Mindset: The convenience trap

Example:

QSlider *slider = new QSlider(12, 18, 3, 13, Qt::Vertical, 0, "Convenience level");

QSlider *slider = new QSlider(Qt::Vertical);
slider->setRange(12, 18);
slider->setPageStep(3);
slider->setValue(13);
slider->setObjectName("Convenience level");

Also: Consider bindings to dynamically typed languages


Mindset: The boolean trap

Pattern:

  1. void doSomething();
  2. void doSomething(bool doSomethingElse = false);
  3. void doSomething(bool doSomethingElse = false, bool orSomethingEntirelyDifferent = false;

Broken rationale: Adds no extra function thus no extra bloat, no need to document another function, no need to adjust the class documentation, simple.

Truth: Adds bloat (to a function, not a class), hard to memorize, leads in non-intuitive code.

Examples:

QWidget: widget->repaint(false);
QWidget: widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding, true);
QTextEdit: editor->insert("what does it mean?", true, true, false);


Mindset: Mind the inheritance structure

Bad example: Good old QToolButton 

In Qt 4:

Another example: QWidget::icon QButton::pixmap QToolButton::iconSet

In Qt 4: QWidget::windowIcon QAbstractButton::icon


Mindset: static polymorphism


Mindset: QA


Naming

  1. General considerations
  2. Naming classes
  3. Naming members
  4. Naming parameters
  5. Naming enums
  6. Naming boolean properties


Naming: General


Ways of finding good names


Naming: Classes


Naming: Members


Naming: Enums


Naming: Boolean properties

  1. Rule of thumb: "is" for singular adjectives, and no prefix otherwise.
  2. Never use the third person.
  3. Never prefix with "has".

Singular adjectives use "is":

Plural adjectives have no prefix:

Verbs have no prefix and don't use the third person ("s"):

Nouns generally have no prefix:

In some cases, having no prefix is misleading, in which case we prefix with "is":


Example: Naming a boolean property

Task: HTML-rendering widget should have a property that indicates whether it underlines links or not.

Obvious start: setLinkUnderline():

Start with getter:

Result: setLinksUnderlined()and linksUnderlined()


Misc


Case study: QProgressBar

Current QProgressBar API in Qt3

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 & progress_str, int progress, int totalSteps);


Case study: QProgressBar


Case study: QProgressBar


Case study: QProgressBar


Case study: QProgressBar

New and improved QProgressBar API

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);